score:3
it's a judgement call, but in most cases, yes, use separate usestate
calls for each of those three pieces of state information (or use usereducer
instead). that way, setting them is simple and straightfoward. e.g.:
const [name, setname] = usestate("");
const onnamechange = e => setname(e.target.value);
<input
type="text"
onchange={onnamechange}
>
class-based components get a setstate
function that accepts partial state updates and merges them into the component's state, but that isn't how usestate
's setter works; it completely replaces the state item.¹ that means if you're using a compound state item as in your question, you have to include all of its parts in every call to the setter — which sets you up to accidentally use stale copies of the parts you're not intentionally updating:
const [formobj, setformobj] = usestate({name: "", link: "", error: ""});
const onnamechange = ({target: {value}}) => setformobj({...formobj, name: value}); // <== usually wrong
the problem there is that the data in formobj
can be out of date. you'd have to use the callback form instead:
const [formobj, setformobj] = usestate({name: "", link: "", error: ""});
const onnamechange = ({target: {value}}) => setformobj(formobj => ({...formobj, name: value}));
the other issue is that if you're using any of those pieces of state as dependencies in a useeffect
/usememo
/usecallback
/etc., it's easy to get that wrong too.
so yes, your manager and tech architect are likely correct, use individual usestate
calls (or usereducer
). but, again, it's a judgement call; you could just always use the callback form of useformobj
.
another option:
you could create a hook for the form object with individual setters for the name, link, and error that accept either a string or an event, like this:
// a reusable utility function: if the argument is an object with a `target` property,
// return `x.target.value`; otherwise, just return `x`.
function unwrapvalue(x) {
if (typeof x === "object" && x.target) {
x = x.target.value;
}
return x;
}
// off-the-cuff, untested, just a sketch
function useformobj(initialformobj = {name: "", link: "", error: ""}) {
const [formobj, setformobj] = usestate(initialformobj);
const setters = useref(null);
if (!setters.current) {
// only true on first call
setters.current = {
setname(name) {
name = unwrapvalue(name);
setformobj(formobj => ({...formobj, name}));
},
setlink(link) {
link = unwrapvalue(link);
setformobj(formobj => ({...formobj, link}));
},
seterror(error) {
error = unwrapvalue(error);
setformobj(formobj => ({...formobj, error}));
},
};
}
return [formobj, setters.current];
}
and then:
const [formobj, {setname, setlink, seterror}] = useformobj();
<input
type="text"
onchange={setname}
>
that's handy when you need to use form objects in more than one component, or you just want to have smaller more easily-testable pieces.
¹ from the documentation (you have to scroll down slightly to the note:):
note
unlike the
setstate
method found in class components,usestate
does not automatically merge update objects. you can replicate this behavior by combining the function updater form with object spread syntax:setstate(prevstate => { // object.assign would also work return {...prevstate, ...updatedvalues}; });
another option is
usereducer
, which is more suited for managing state objects that contain multiple sub-values.
score:1
it is true that usestate replaced the state completely, but you can create your own custom hooks which you can try to update the state using spread operator, eg {...obj, ...values} so this way you don't have to update the whole object, spread operator will take care.
i think you can go for custom hooks and manage this.
score:5
i realize this is an old question, but if anyone else is trying to figure this out, i'd like to say i've built a whole production app this way. every page (it was a next.js app) used one usestate and in it was an objected with many properties. error, form data, loading, etc.
if you read the react docs they recommend against it because of possible performance issues due to the fact that an object is re-evaluated every time. even with that, the app runs quickly. i think we overcomplicate things way too often. a reducer would have been better, sure. but the speed you can develop with not going into abstract complexities by keeping things simple has a lot less bugs and performance issues than people seem to think. remember, the react team are smart. they're not going to set you up for failure. if it really hindered performance in a great way and it was a basic and fundamental thing like usestate is, they'd go out of their way to prevent you from doing such things.
by the way this production app services hundreds of active users every week for ordering food and we've had no complaints and heard only good things.
Source: stackoverflow.com
Related Query
- Should I use separate useState for each property
- Should I use React Router redirect OR package.json "homepage" property for setting a relative path as homepage in my React app
- TypeScript: What property should I use for Element type?
- React Context vs React Redux, when should I use each one?
- What is the difference between .ts and .tsx extensions. Both are used as extensions for typescript files in react. So where should we use them?
- Should I use react-router for a tabs component?
- React Native - Why we use the tintColor property for Image component?
- Should I use react-loadable or loadable-components for code splitting?
- Parcel: using separate SCSS files for each React component, but with a variables file
- Why to use separate CSS files for components in React.js
- How to use a different delay for each item with React transition group?
- why should I not use CDN for react & babel?
- Should I use useEffect() for dom manipulation or just do it directly?
- What are the scenarios one should use isRequired for PropType vs defaultProps in React Application
- React + Redux + Router - should I use one state/store for all pages/components?
- How should I use "redux-thunk" for Async Initial state ? (react/redux)
- Should I use a controlled React component for the input field (security wise)?
- Use case for useLayoutEffect + useState vs useMemo
- Should I use useRef for storing one-time initialised data?
- How to handle separate state for each browser tab in reactjs?
- How to use different icons for each tab with createBottomTabNavigator?
- Better way to use useState hook for setting the boolean state in React
- ReactJS good practices using Hooks and Redux. Should I use useSelector in each component, or I should pass via props?
- Webpack 4 Code splitting generating separate vendor files for each route
- Should I use JS events or CSS media queries for responsive tables with React?
- Why do I get a warning that says " each child in a list should have a unique key prop" but I already have a unique key prop for the child component
- Is it a bad practice to use state in a React component if the data will not change? Should I use a property on the class instead?
- What type should I use for input react-bootstrap event with TypeScript?
- should i use media queries for responsive design with react?
- Why use ES6 computed property syntax for object setState?
More Query from same tag
- When returning content from getInitialProps, is there a method to check for empty?
- I want to use embedded Browser inside of my WebPage in React
- Integrate Stripe payment method into react JS
- React-Redux TypeError: Cannot read property 'setStatus' of undefined
- How to Create Country and City dropdowns data with json - PrimeReact
- why split app and vendor js code fails in webpack & react
- How can I not display elements that are repeated?
- Print divs on same line height css
- React not taking a default page at load
- How can a component send a message to its sibling without moving the state up the component tree?
- Webpack/babel not compiling ReactDOM.render
- How to prefer a css file over another?
- i'm trying to make a axiosGET request to my react component, i get the object on the console.log. But when i try to render it i get a "is not defined"
- How to automaticaly send ALL errors from javascript to Sentry?
- How to do server rendering with react-router 1.0.0?
- Keying react lists with dynamic properties
- React wrapper component not working as expected
- How to handle side effects when migrating from Redux to React Context API + hooks
- Javascript React Image Glass Magnifier
- How to set up dynamic row height in react-virtualized List?
- How to update a complex array?
- Ant design editable Paragraph value become null when onChange is triggered
- Showing a default value for a select control using react and material-ui control
- How to use a function on onClick instead of onChange?
- Received event.value is undefined on text input change
- Data structure for highlights of words in a string with JavaScript
- How do I render a single Item fetched from the API ReactJS
- React Append a button on radio button selection
- How to implement route-based code splitting when using useRoutes
- show only the message of error object in fetch api javascript