|
| 1 | +# @nexpy/react-easy-context-api |
| 2 | + |
| 3 | +[](https://www.npmjs.com/package/@nexpy/react-easy-context-api) |
| 4 | +[](https://bundlephobia.com/result?p=@nexpy/react-easy-context-api) |
| 5 | + |
| 6 | +## Introduction |
| 7 | + |
| 8 | +Many times when using the React Context API we create unnecessary repetitive and extensive code. |
| 9 | + |
| 10 | +And, React Context and useContext is often used to avoid prop drilling, |
| 11 | +however it's known that there's a performance issue. |
| 12 | +When a context value is changed, all components that useContext |
| 13 | +will re-render. |
| 14 | + |
| 15 | +To solve this issue, |
| 16 | +[useContextSelector](https://github.com/reactjs/rfcs/pull/119) |
| 17 | +is proposed and later proposed |
| 18 | +[Speculative Mode](https://github.com/reactjs/rfcs/pull/150) |
| 19 | +with context selector support. |
| 20 | +This library provides an easy way to build and use the Context API with these issues fixed. |
| 21 | + |
| 22 | +This package is constructed above the [use-context-selector](https://github.com/dai-shi/use-context-selector). |
| 23 | + |
| 24 | +## Install |
| 25 | + |
| 26 | +This package requires some peer dependencies, which you need to install by yourself. |
| 27 | + |
| 28 | +```bash |
| 29 | +yarn add @nexpy/react-easy-context-api scheduler |
| 30 | +``` |
| 31 | + |
| 32 | +Notes for library authors: |
| 33 | + |
| 34 | +Please do not forget to keep `"peerDependencies"` and |
| 35 | +note instructions to let users to install peer dependencies. |
| 36 | + |
| 37 | +## Technical memo |
| 38 | + |
| 39 | +To make it work like original React context, it uses |
| 40 | +[useReducer cheat mode](https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks) intentionally. |
| 41 | +It also requires `useContextUpdate` to behave better in Concurrent Mode. |
| 42 | +(You don't need to use it in Legacy Mode.) |
| 43 | + |
| 44 | +## Usage |
| 45 | + |
| 46 | +```tsx |
| 47 | +// cats-context.tsx |
| 48 | +import { useState, FC } from 'react' |
| 49 | + |
| 50 | +import { createContext } from '@nexpy/react-easy-context-api' |
| 51 | + |
| 52 | +type MyContext = { |
| 53 | + pettedCats: number |
| 54 | + currentCats: number |
| 55 | +} |
| 56 | + |
| 57 | +const { useSelector, Provider } = createContext<MyContext>({ |
| 58 | + pettedCats: 0, |
| 59 | + currentCats: 0, |
| 60 | +}) |
| 61 | + |
| 62 | +const CatsProvider: FC = ({ children }) => { |
| 63 | + const [pettedCats, setPettedCats] = useState(0) |
| 64 | + const [currentCats, setCurrentCats] = useState(0) |
| 65 | + |
| 66 | + // ... your context logic :) |
| 67 | + |
| 68 | + return ( |
| 69 | + <Provider |
| 70 | + value={{ |
| 71 | + pettedCats, |
| 72 | + currentCats, |
| 73 | + }} |
| 74 | + > |
| 75 | + {children} |
| 76 | + </Provider> |
| 77 | + ) |
| 78 | +} |
| 79 | + |
| 80 | +export { CatsProvider, useSelector } |
| 81 | + |
| 82 | +// ... in your components |
| 83 | +const CatsPetted = () => { |
| 84 | + const catsPettedNumber = useSelector(state => state.pettedCats) |
| 85 | + |
| 86 | + return <p>Petted cats: {catsPettedNumber}</p> |
| 87 | +} |
| 88 | + |
| 89 | +const CurrentCats = () => { |
| 90 | + const currentCatsNumber = useSelector(state => state.currentCats) |
| 91 | + |
| 92 | + return <p>Current cats: {currentCatsNumber}</p> |
| 93 | +} |
| 94 | + |
| 95 | +const App = () => ( |
| 96 | + <CatsProvider> |
| 97 | + <CatsPetted /> |
| 98 | + <CurrentCats /> |
| 99 | + </CatsProvider> |
| 100 | +) |
| 101 | +``` |
| 102 | + |
| 103 | +## API |
| 104 | + |
| 105 | +### createContext |
| 106 | + |
| 107 | +This creates a special context. |
| 108 | + |
| 109 | +#### Parameters |
| 110 | + |
| 111 | +- `defaultValue` **Value** |
| 112 | + |
| 113 | +#### Examples |
| 114 | + |
| 115 | +```ts |
| 116 | +import { createContext } from '@nexpy/react-easy-context-api' |
| 117 | + |
| 118 | +type PersonContext = { |
| 119 | + firstName: string |
| 120 | + familyName: string |
| 121 | +} |
| 122 | + |
| 123 | +const PersonContext = createContext<PersonContext>({ firstName: '', familyName: '' }) |
| 124 | +``` |
| 125 | + |
| 126 | +### Returns: |
| 127 | + |
| 128 | +#### useSelector |
| 129 | + |
| 130 | +This hook returns context selected value by selector. |
| 131 | + |
| 132 | +It will trigger re-render if only the selected value is referentially changed. |
| 133 | + |
| 134 | +The selector should return referentially equal result for same input for better performance. |
| 135 | + |
| 136 | +##### Parameters |
| 137 | + |
| 138 | +- `selector` **function (value: Value): Selected** |
| 139 | + |
| 140 | +##### Examples |
| 141 | + |
| 142 | +```tsx |
| 143 | +const MyComponent = () => { |
| 144 | + const firstName = PersonContext.useSelector(state => state.firstName) |
| 145 | + |
| 146 | + return <p>{firstName}</p> |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +#### useContext |
| 151 | + |
| 152 | +This hook returns the entire context value. |
| 153 | +Use this instead of React.useContext for consistent behavior. |
| 154 | + |
| 155 | +##### Examples |
| 156 | + |
| 157 | +```tsx |
| 158 | +const MyComponent = () => { |
| 159 | + const person = PersonContext.useContext() |
| 160 | + // ... |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +#### useContextUpdate |
| 165 | + |
| 166 | +This hook returns an update function that accepts a thunk function. |
| 167 | + |
| 168 | +Use this for a function that will change a value in |
| 169 | +[Concurrent Mode](https://reactjs.org/docs/concurrent-mode-intro.html). |
| 170 | +Otherwise, there's no need to use this hook. |
| 171 | + |
| 172 | +##### Examples |
| 173 | + |
| 174 | +```tsx |
| 175 | +const MyComponent = () => { |
| 176 | + const update = PersonContext.useContextUpdate() |
| 177 | + |
| 178 | + update(() => setState(...)); |
| 179 | + // ... |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +#### BridgeProvider |
| 184 | + |
| 185 | +This is a Provider component for bridging multiple react roots. |
| 186 | + |
| 187 | +#### Context |
| 188 | + |
| 189 | +The special context created by createContext hook. |
| 190 | + |
| 191 | +#### Provider |
| 192 | + |
| 193 | +The provider you need to use to apply the context. |
| 194 | + |
| 195 | +##### Parameters |
| 196 | + |
| 197 | +- `$0` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** |
| 198 | + - `$0.context` |
| 199 | + - `$0.value` |
| 200 | + - `$0.children` |
| 201 | + |
| 202 | +##### Examples |
| 203 | + |
| 204 | +```tsx |
| 205 | +import { createContext } from '@nexpy/react-easy-context-api' |
| 206 | + |
| 207 | +type PersonContext = { |
| 208 | + firstName: string |
| 209 | + familyName: string |
| 210 | +} |
| 211 | + |
| 212 | +const { Context, useBridgeValue, BridgeProvider } = createContext<PersonContext>({ |
| 213 | + firstName: '', |
| 214 | + familyName: '', |
| 215 | +}) |
| 216 | + |
| 217 | +const App = () => { |
| 218 | + const valueToBridge = useBridgeValue() |
| 219 | + |
| 220 | + return ( |
| 221 | + <> |
| 222 | + <BridgeProvider context={Context} value={valueToBridge}> |
| 223 | + {children} |
| 224 | + </BridgeProvider> |
| 225 | + </> |
| 226 | + ) |
| 227 | +} |
| 228 | +``` |
| 229 | + |
| 230 | +#### useBridgeValue |
| 231 | + |
| 232 | +This hook return a value for BridgeProvider. |
| 233 | + |
| 234 | +## Limitations |
| 235 | + |
| 236 | +- In order to stop propagation, `children` of a context provider has to be either created outside of the provider or memoized with `React.memo`. |
| 237 | +- Provider trigger re-renders only if the context value is referentially changed. |
| 238 | +- Neither context consumers or class components are supported. |
| 239 | +- The [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) issue can't be solved in userland. |
| 240 | +- Tearing is only avoided if all consumers get data using `useSelector`. If you use both props and selector to pass the same data, they may provide inconsistence data for a brief moment. |
| 241 | + |
| 242 | +## Examples |
| 243 | + |
| 244 | +[See this example file](https://github.com/nexpy-io/react-easy-context-api/blob/main/examples/cats.md). |
0 commit comments