Comments (5)
useReducer is beneficial when dealing with complex state logic and multiple sub-values; a very deep object for example. As the documentation says, it makes testing and debugging easier.
I'm trying to convey that for the described use case, useState
does a better job than useReducer
. Please see my example in the first message.
My proposal is to deprecate useReducer
and provide more examples for useState
.
from react.
If we were going to remove anything, it would be useState, because useState is basically a helper for useReducer to handle the basic cases. In fact, useState is implemented with useReducer under the hood, with a basic reducer provided.
What you've provided is essentially a reducer. The example you give can be replaced with useReducer, which is closely equivalent to what useState is doing:
const myModule = {
increment: (state) => {
return {count: state.count + 1};
},
decrement: (state) => {
return {count: state.count - 1};
}
};
const reducer = (state, action) => {
return action(state);
} ;
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch(myModule.increment)}>Increment</button>
<button onClick={() => dispatch(myModule.decrement)}>Decrement</button>
</div>
);
};
But both this and the useState example are annoying because now you'll need to pass both the dispatch
and the myModule
functions around to children. You can solve this by wrapping the dispatches:
const myModule = {
increment: (state) => {
return {count: state.count + 1};
},
decrement: (state) => {
return {count: state.count - 1};
}
};
const reducer = (state, action) => {
return action(state);
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const dispatcher = useMemo(
() => ({
increment: () => dispatch(myModule.increment),
decrement: () => dispatch(myModule.decrement),
}),
[]
);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatcher.increment()}>Increment</button>
<button onClick={() => dispatcher.decrement()}>Decrement</button>
</div>
);
};
But that's a lot of indirection to follow. It's less indirection to write as typical reducer:
const reducer = (state, action) => {
switch (action) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw Error('Unknown action: ' + action.type);
}
};
const SCounter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch("increment")}>Increment</button>
<button onClick={() => dispatch("decrement")}>Decrement</button>
</div>
);
};
And if you want to pass more values to the action, the convention is to use type
on the action:
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw Error('Unknown action: ' + action.type);
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({type: "increment"})}>Increment</button>
<button onClick={() => dispatch({type: "decrement"})}>Decrement</button>
</div>
);
};
Of course, if you want to use functions, you can still wrap them:
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
throw Error('Unknown action: ' + action.type);
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const {increment, decrement} = useMemo(() => {
return {
increment: (...args) => dispatch({type: "increment", ...args}),
decrement: (...args) => dispatch({type: "decrement", ...args}),
}
}, []);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => increment()}>Increment</button>
<button onClick={() => decrement()}>Decrement</button>
</div>
);
};
But notice that there's still less indirection than before. This also scales up better because the single reducer function operates as a function that accepts state, and an action and returns the same state object. The "switch" here is a benefit because you're able to see a list of all the actions that can be taken on the state, and the result of the action. For complex state objects, the function example may be difficult to follow and result in hard to see bugs, because essentially each function in your object is a separate reducer.
from react.
Hi,
useReducer
is beneficial when dealing with complex state logic and multiple sub-values; a very deep object for example. As the documentation says, it makes testing and debugging easier. So for small and simple states useState
does the job. If you're incrementing the complexity of your state you should start thinking in useReducer
Anyway I believe this sort of questions have a better place: https://react.dev/community
from react.
I mean, yeah got your point. What I tried to say was that your concrete use case was simple enough to use useState
instead of useReducer
. No need to deprecate it; just don't use it if you don't need it. Both hooks serve different purposes. As you can see in the source code, they do work in a similar way but with a couple of differences.
You may want to check the documentation for more useReducer
examples in order to catch the idea of its purpose:
- https://react.dev/reference/react/useReducer (This one includes an example with
Immer
) - https://legacy.reactjs.org/docs/hooks-reference.html#usereducer (this one has very practical examples regarding lazy initialization and the combo with
useContext
)
Regarding to "...provide more examples for useState
". It's up to you how you're going to use useState
, I'm sure there're a lot of cool recipes out there (including yours). I mean you can make an API request in the initial state; doesn't mean you should do it tho ¯_(ツ)_/¯.
from react.
Conside if you are building an ecommerce site or something more complex, at that point of time the useReducer
will be of more use than useState. If you consider a simple increment, decrement separate module will be better option. Ensuring that the state remains consistent and predictable across all actions and components can be difficult with a separate module, especially as the application grows and evolves over time
from react.
Related Issues (20)
- React compiler requires that expressions referenced in handler code are always resolvable HOT 1
- 2024 谷歌蜘蛛池出租:谷歌蜘蛛池出租厂家
- `react-compiler-healthcheck` prints "StrictMode usage not found." in Next.js project with `reactStrictMode: true` in `next.config.js`
- [React 19] react compiler warns about mutating ref in specific case
- [React 19] react-compiler warns when mutating globals inside useCallback
- [React 19] `transformOrigin` is Missing From Type `SVGAttributes`.
- React compiler healthcheck (and ESLint plugin) ignores all files if a babel.config.js file is present HOT 8
- react-compiler transformed conditional useCallback call incorrectly HOT 8
- Bug: Cannot run compiler playground locally HOT 2
- [Compiler Bug]: value read during render does not update when using `react-hook-form` `.watch` API HOT 4
- Bug: Truffle firefox extension causes a uncaught runtime exception on fresh react app HOT 1
- "This page doesn’t appear to be using React." all the time! HOT 2
- [Compiler Bug]: Compiler disagrees with `exhaustive-deps` linter on whether `React.Ref<T>.current` must be in a deps array HOT 1
- [Compiler Bug]: Compiler doesn't bail out when reading or writing `ref.current` during a render HOT 3
- [DevTools Bug]: Version 5.2 shows incorrect source for all the components HOT 2
- [Compiler Bug]: false positive when using hooks in a nested component HOT 8
- Bug: Displayed content is not handled properly. HOT 3
- [Compiler Bug]: don't uses dependecies of useMemo HOT 1
- [Compiler Bug]: Values used as indexes are not memoized HOT 4
- [Compiler Bug]: Issue with React Hook Form library HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from react.