Skip to content

Instantly share code, notes, and snippets.

@jeongtae
Last active February 29, 2020 04:07
Show Gist options
  • Select an option

  • Save jeongtae/fe42d7e1db89919643a34cb3d7b81e8a to your computer and use it in GitHub Desktop.

Select an option

Save jeongtae/fe42d7e1db89919643a34cb3d7b81e8a to your computer and use it in GitHub Desktop.
My way to use Context API in React
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>
);
}
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 };
}
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 };
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