Last active
February 29, 2020 04:07
-
-
Save jeongtae/fe42d7e1db89919643a34cb3d7b81e8a to your computer and use it in GitHub Desktop.
My way to use Context API in React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React from "react"; | |
| import { ExampleProvider } from "./ExampleContext"; | |
| import MyComponent from "./MyComponent"; | |
| /** uses: `<MultipleProvider providers={[FirstProvider, SecondProvider, ...]}>` */ | |
| const MultipleProvider = ({ providers = [], children }) => { | |
| const reducer = (previous, provider) => React.createElement(provider, { children: previous }); | |
| const reduced = providers.reduce(reducer, children); | |
| return reduced; | |
| }; | |
| export default function App() { | |
| return ( | |
| <ExampleContext> | |
| <MyComponent /> | |
| </ExampleContext> | |
| ); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { | |
| createContext as createReactContext, | |
| useContext as useReactContext, | |
| useState | |
| } from "react"; | |
| export default function(states, actions) { | |
| const givenStates = { ...states }; | |
| const givenActions = { ...actions }; | |
| // create Context and result hook | |
| const reactContext = createReactContext(); | |
| const useContext = () => useReactContext(reactContext); | |
| // wrap actions | |
| const wrappedActions = {}; | |
| let statesForAction = {}; | |
| Object.entries(givenActions).forEach(([actionName, action]) => { | |
| wrappedActions[actionName] = (...params) => { | |
| action(statesForAction, ...params); | |
| }; | |
| }); | |
| // get Provider and result provider | |
| const ReactProvider = reactContext.Provider; | |
| const ContextProvider = ({ children }) => { | |
| const [states, setStates] = useState(givenStates); | |
| // add state setters | |
| statesForAction = { ...states }; // change states reference for actions | |
| for (const stateName in states) { | |
| // add setter for each states | |
| const capitalizedName = `${stateName.charAt(0).toUpperCase()}${stateName.slice(1)}`; | |
| // eslint-disable-next-line | |
| statesForAction[`set${capitalizedName}`] = param => { | |
| const oldState = states[stateName]; | |
| const newState = typeof param === "function" ? param(oldState) : param; | |
| if (oldState !== newState) { | |
| setStates({ ...states, [stateName]: newState }); | |
| states[stateName] = newState; | |
| statesForAction[stateName] = newState; | |
| } | |
| }; | |
| } | |
| // add setter that changes multiple states at once | |
| statesForAction.setMultiple = param => { | |
| const oldStates = states; | |
| const statesToSet = typeof param === "function" ? param(oldStates) : param; | |
| // compare with old states | |
| let isDiffer = false; | |
| for (const stateName in statesToSet) { | |
| if (stateName in oldStates && statesToSet[stateName] !== oldStates[stateName]) { | |
| isDiffer = true; | |
| break; | |
| } | |
| } | |
| // if it is different | |
| if (isDiffer) { | |
| setStates({ ...oldStates, ...statesToSet }); | |
| for (const stateName in statesToSet) { | |
| states[stateName] = statesToSet[stateName]; | |
| statesForAction[stateName] = statesToSet[stateName]; | |
| } | |
| } | |
| }; | |
| return ( | |
| <ReactProvider value={{ states: states, actions: wrappedActions }}>{children}</ReactProvider> | |
| ); | |
| }; | |
| // Return useContext function and ContextProvider component | |
| return { useContext, ContextProvider }; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import createContext from "./createContext"; | |
| export const { useContext: useExampleContext, ContextProvider: ExampleProvider } = createContext( | |
| { | |
| // states | |
| name: "Jeongtae", | |
| age: 25 | |
| }, | |
| { | |
| // actions | |
| sayHello(states, message) { | |
| // reading a state | |
| const { name } = states; | |
| return `Hello ${name}, ${message}`; | |
| }, | |
| addAge(states, amount) { | |
| // setting a state based on existing value | |
| const { setAge } = states; | |
| setAge(age => age + amount); | |
| }, | |
| makeNickname(states) { | |
| // setting a state with new value | |
| const { setName, age } = states; | |
| setName(age > 40 ? "Old man" : "Young man"); | |
| }, | |
| clear(states) { | |
| // setting multiple state at once | |
| // this example will prevents components from being updated multiple times | |
| // when you call `setState` multiple times. | |
| const { setMultiple } = states; | |
| setMultiple({ name: "Nobody", age: 0 }); | |
| } | |
| } | |
| ); | |
| export { useExampleContext, ExampleProvider }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React from "react"; | |
| import { useExampleContext } from "./ExampleContext"; | |
| export default () => { | |
| const { | |
| states: { name, age }, | |
| actions: { sayHello, addAge /*, makeNickname, clear*/ } | |
| } | |
| return ( | |
| <> | |
| <h1>{sayHelo("How are you doing?")}</h1> | |
| <p>Name: {name}</p> | |
| <p>Age: {age}</p> | |
| <button onClick={() => addAge(1)}>Add Age</button> | |
| </> | |
| ); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment