Using hooks with object or single value? - javascript

I can't find answer so, this is my question. What is better practice using hooks like that with spread operator?
const [user, setUser] = useState({name: 'John', email: 'john#john.pl'})
setUser(prev => {...prev, name: 'New name'})
Or making state per properties?
const [name, setName] = useState('John')
setName('New name')
const [email, setEmail] = useState('john#john.pl')
setEmail('New email')
What is better option and why?

Clearly the 2nd one since you don't need to pass around the whole user state on each state update but just the user's name or email. Try to keep it simple where ever you can.

please read this info in react docs.
https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables
The both approaches has pros and cons.
Some important hightligts from docs:
we recommend to split state into multiple state variables based on
which values tend to change together.
Both putting all state in a single useState call, and having a
useState call per each field can work. Components tend to be most
readable when you find a balance between these two extremes, and group
related state into a few independent state variables. If the state
logic becomes complex, we recommend managing it with a reducer or a
custom Hook.

Usually it's better to have simple state when using hooks, since setState() works a bit different than this.setState() in class components - it not merges changes, but just update those:
// in class component
this.setState({ name: 'Hello' }); // update only name field of state
// in functional component
setState({ name: 'Hello' }); // sets { name: 'Hello' } as new state
For complex state you can use useReducer() hook.

hooks state easy to use than setState. You can use hooks with spread operator like this
const [user, setUser] = useState({name: 'John', email: 'john#john.pl'});
setUser({...user, name: 'New name'});

Related

React functional component access entire state

In a classical component the entire state is available as a single object this.state = {}, I was wondering if there's a similar way you could access the state in a functional component? You can access individual properties via their variable names but I'm wondering if there's a way to access all of them at once as if they were stored in an overarching object, similar to a class component.
No, there's no consolidated state object you can get access to that has all of the state members you've set up via useState.
You could store all of your component state in a single object if you want (this is even mentioned in the useState docs when talking about callback updates). For instance:
const [state, setState] = useState({
name: "",
profession: "",
age: null,
// ...
});
To update any individual state member, you have to handle creating the entire new object yourself (state setters from useState don't do the merging that Component.prototype.setState does). For instance, setting the age:
setState(state => ({
...state,
age: 42,
}));
It's important to use the callback form (as is always the case when setting state based on existing state).
But unless you do that, no, there's no one single object you have access to.

Assigning value at useState() declaration vs useEffect()

I have been using these two ways interchangeably however I am not sure which one is the more correct. their behavior seems to be the same but I am sure there is a use case for each. Anyone can help me understand what's the proper use for each case?
const [customer, setCustomers] = useState(props.location.state);
useEffect(() => {
setCustomers(props.location.state);
}, []);
You should normally stick to the first one. Calling the setter of useState may lead to undesired re-renders and decreased performance.
In the first block the customer is initialised directly and no re-render happens. The setCustomer method will change the state and rerender the component. In the end the whole function will run twice which you can verify with a console.log.
const [customer, setCustomers] = useState(0);
useEffect(() => {
setCustomers(15);
}, []);
console.log(customer) // will first output 0 and then 15
Assuming in the second case, you have this as your useState statement:
const [customer, setCustomers] = useState();
The second one sets the value of customer on componentDidMount. So in the initial render, you will not have the appropriate value in your customer variable.
But yes, very soon after that the correct value will be set because of the code written in useEffect.
To clear it up, there will be 2 renders here (because the state variable value changes). In the first one, that won't be the case since the state variable has only one value from beginning.
The first one is more effective.
const [customer, setCustomers] = useState(props.location.state);
If you use second one (by using useEffect), your component will be re-rendered again.
That's, your state variable customer will be updated in useEffect after DOM is initially rendered, this leads the 2nd re-render of the component.
But if you want customer to be updated by props.location.state, you need to add useEffect hook like the following.
useEffect(()=> {
setCustomers(props.location.state);
}, [props.location.state]);
Setting the state's default value upon declaring it is probably the more correct way to go, since it does not trigger a re-render.
Every time you call a setState your component will be re-rendered, so when you do so in the useEffect, you will trigger an unnecessary re-render upon the component mounting, which could be avoided by doing the good ol'
const [value, setValue] = useState(props.location.state)
While of course there are exceptions and many different use cases, setting an initial state in a useEffect is more useful, for example, when you have values you'd expect to change regardless of your component (for example from an external asynchronous API call):
const [value, setValue] = useState(valueExpectedToChange)
useEffect(() => {
setValue(valueExpectedToChange) // will trigger the rerender only when valueExpectedToChange changes
}, [valueExpectedToChange])

How can I update multiple states at a time with useState hook?

In functional components, we use useState as opposed to this.setState for classComponents. But as far as I know, you can only set one state at a time with useState, while setState lets you set multiple states at a time, (e.g. this.setState({a1: true, a2: false})).
Does this mean if you wanted to set two states simultaneously with useState, you'd get a double re-render, which is ineffficient? Is there a way to get around this?
AFAIK that's right but you can also think that in most cases you just group states in the way they are correlated instead of having a big this.state with bunch of properties without being related with each other, that also sucks in some way
So commonly you won't be updating two states every time and if you do that, most probably you want to mix those states into one.
So supposing you have these two states:
const [state1, setState1] = useState(...);
const [state2, setState2] = useState(...);
And you always in your onChange function (or whatever) do:
const onChange = () => {
setState1(...);
setState2(...);
}
You should mix those into one state like this:
const [state, setState] = useState({ state1: ..., state: ...}); // or it could be other thing than an object...

How to prevent race conditions with react useState hook

I have a component that uses hooks state (useState) api to track the data.
The object looks like this
const [data,setData] = React.useState({})
Now I have multiple buttons which make API requests and set the data with the new key
setAPIData = (key,APIdata) => {
const dup = {
...data,
[key]:APIdata
}
setData(dup)
}
Now if I make multiple requests at the same time , it results in race conditions since setting state in react is asynchronous and I get the previous value.
In class-based components, we can pass an updater function to get the updated value, how to do this hooks based component.
You must use setData with a function as its argument. Then it will always get the previous state, no matter what order it will be called.
const [data,setData] = React.useState({})
setData(prevData => ({
...prevData,
[key]: APIdata
}));
Documentation: somewhat hidden in hook api reference.
reactjs.org/docs/hooks-reference.html#functional-updates

Selectors in Redux - What does it mean for a React Component to know about redux state?

Selectors are (among other reasons) used to hide state from components. Is this statement correct?
Say I have 2 reducers:
currentUser:
{
currentUserId: 'abc'
}
allUsers
{
byId: {
{
abc: {
name: "John"
age: 21
}
def: {
name: "Dave"
age: 51
}
ghi: {
name: "Gunners"
age: 98
}
}
},
allIds : ['abc','def','ghi'] //this is a list; users have a certain order, that is preserved here
}
Say I now want to get some derived state. I will use a selector so that my component does not have to deal with the reducers state directly. This (apart from memoization/performance) is part of the rationale behind selectors, correct? So I implement a selector that returns a user to my mapStateToProps function.
But: now my component still has to know about how the user object looks like? E.g. that there is obj.name and obj.age - so how and where does knowing about state begin and where does it end? Or, alternatively, and a bit more likely I presume, what am I misunderstanding here?
I am kind of confused about your question. Can't comment...
It's a good idea to use selectors because it can make the code more manageable, or you just need something more specific from the state.
For instance, if you have a notification reducer that contains both read and unread notifications you could create a selector that will handle removing the read notifications so you can only display the unread notifications.
So mapStateToProps will push anything that is given to it. Whatever is returned from your selector will be pushed into the props of the component.
If you wanted it to only know about name and age you can really easily do this without creating a separate selector.
// taking into consideration the code you posted
const mapStateToProps = (state) => ({
users: state.allUsers.byId
})
now mapStateToProps will only know about name and age.

Categories