Trying to catch up on the React Hooks. I'm reading that they recommend the use of the Hook useReducer
when dealing with a complex state
. But my doubt starts from the following scheme:
Using React + Typescript, suppose I have a state with several fields (I'll give an example with classes):
type Person = {
username: string,
email: string
}
type Pet = {
name: string,
age: number
}
this.state: MyState = {
person: Person,
pet: Pet,
loading: boolean
}
If I wanted to handle this state with a new Hooks-based approach, I could think of several options:
Option 1: using a Hook useState
for each field
const [person, setPerson] = useState<Person>(null)
const [pet, setPet] = useState<Pet>(null)
const [loading, setLoading] = useState<boolean>(false)
This method has the disadvantage of low scalability and some of my real states have at least 15 fields, is unmanageable.
Option 2: Using a single setState
for the entire object
const [state, setState] = useState<MyState>({
person: null,
pet: null,
loading: false
})
This is the method that seems simplest to me, where I can simply do setState((prev) => {...prev, person: {username: 'New', email: '[email protected]'}})
or adapt it to any field modification. I can even update several fields at once.
Option 3: use a useReducer
for each of the complex fields by passing a specific reducer for each one, use useState
for the simple ones
const [person, dispatchPerson] = useReducer<Person>(personReducer)
const [pet, dispatchPet] = useReducer<Pet>(petReducer)
const [loading, setLoading] = useState<boolean>(false)
I find this one manageable, but I don't see the point of having to set up a reduce function with a multi-line switch, in addition to the tedious process of setting the dispatching types in Typescript for each reduce function when you could just use setState and be done with it.
Option 4: use one useReducer
for the entire state
const [state, dispatch] = useReducer(generalReducer)
The main problem with this is the type of the reducer, think of 15 fields, where all the types and the structure of the information to update them are different. Specifying the types in Typescript does not scale or is unclear. There are several articles about this and none of them solve the problem in a clean way (example 1), or they are extremely simple and don't apply to the problem (example 2).
What would be the best way to handle this type of cases? Where the number of fields in the state is large and can have several levels of depth. Are there good practices or any official examples that represent these cases? The examples with a number field to handle a simple counter that bloggers or official documentation people like so much are not being very helpful.
Any light on the subject would be more than welcome! Thanks in advance and sorry about my English
Syntax. The useReducer Hook accepts two arguments. The reducer function contains your custom state logic and the initialState can be a simple value but generally will contain an object. The useReducer Hook returns the current state and a dispatch method.
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
The most-used hook in React is useState. We can use the useState hook inside a functional component and that will make our component associated with that state in particular. The useState hook is a simple function that we can pass an initial value.
The useReducer Hook is used to store and update states, just like the useState Hook. It accepts a reducer function as its first parameter and the initial state as the second.
I think your observations are spot on.
I think you should use Option 1 for simple state (e.g. you have only a few items to keep track of), and Option 2 for complex state (lots of items, or nested items).
Options 1 and 2 are also the most readable and declarative.
Option #2 is talked about here: https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables
useReducer is more useful when you have multiple types of actions. If you're just updating state (one type of action), then actions are overkill. Also, useReducer is useful if you're performing calculations or transformations based on previous state (not just replacing part of state). If you're familiar with Redux, useReducer is a simplified version of Redux principles.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With