eolas/zk/Application_state_management_with_React_hooks.md

99 lines
2.6 KiB
Markdown
Raw Normal View History

2022-07-13 15:20:04 +01:00
---
tags:
- javascript
- react
---
# Application state management
2024-06-15 11:30:03 +01:00
Although [React_useReducer](React_useReducer.md) and [React_useContext](React_useContext.md) have
many sound use cases by themselves, when they are combined they offer a way to
acheive a system of global state management, without utilising third-party
libraries like Redux.
2022-07-13 15:20:04 +01:00
## Requirements
The Context API combined with a reducer addresses the following needs:
- be able to consume global state from anywhere in the component hierarchy
without prop-drilling
- update global state from within child components from anywhere in the
component hierarchy
- access, maintain and update complex structured data (arrays/objects) not just
single values, as global state
2022-07-13 15:20:04 +01:00
## Implementation
In essence the approach is as follows:
- Create a context provider that houses the global state object
- Attach this state to a reducer function that operates on the state object
through dispatch methods
2022-07-13 15:20:04 +01:00
First off, our initial state and overall state object:
```js
const initialState = {
count: 0,
};
```
Next, we create our context. This is what we will invoke when we want to consume
our state.
2022-07-13 15:20:04 +01:00
```js
export const CounterContext = React.createContext({});
```
Now we need a reducer that will handle the state updates. We will just use the
same setup as we used in the example of
2024-06-15 11:30:03 +01:00
[React_useReducer](React_useReducer.md#refining-the-syntax):
2022-07-13 15:20:04 +01:00
```js
function reducer(state, action) {
switch (action.type) {
case "increase":
return { ...state, counter: state.counter + 1 };
2022-07-13 15:20:04 +01:00
break;
case "decrease":
return { ...state, counter: state.counter - 1 };
2022-07-13 15:20:04 +01:00
break;
case "increase_by_payload":
return { ...state, counter: state.counter + action.payload };
2022-07-13 15:20:04 +01:00
default:
throw new Error();
}
return newState;
}
```
Finally, we need to make a provider as a top-level component that receives the
reducer's `state` and `dispatch` methods as props:
2022-07-13 15:20:04 +01:00
```js
export const CounterProvider = ({children}) => {
// We pass our reducer function and initial state to useReducer as params
const [state, dispatch] = React.useReducer(reducer, initialState)
// We create our provider and pass the reducer state and update method as props. This is the provider to the CounterContext consumer
<CounterContext.Provider value={{state, dispatch}}>
{children}
</CounterContext.Provider>
}
```
Now whenever we want to consume or update our global state, we can invoke the
context within a lower-level component, for example:
2022-07-13 15:20:04 +01:00
```js
const {state, dispatch} = useContext(CounterContext);
dispatch({
type 'increase_by_payload',
payload: 4
})
console.log(state.counter) // 4
```