Update single value in Redux state - reactjs

I have a page with several components on it, one of the components reads redux state.
When there some changes occur on the redux object component it reloads, shows the spinner and all the data on the component gets updated.
But my task is to update one single value in the redux object state without having to rerender and refresh everything.
For instance here is the redux object, which holds an array of objects with some data.
I need to update one value state in a certain object let say the last one.
But I need to do it without having to update all-state.
state:{
0:{
name : 'some name.....',
surname: 'some surname',
age: '23',
phone: '+12345678'
state: 'ON'
},
1:{
name : 'some name.....',
surname: 'some surname',
age: '23',
phone: '+12345678'
state: 'ON'
},
2:{
name : 'some name.....',
surname: 'some surname',
age: '23',
phone: '+12345678'
state: 'OFF' <-- I need to update only this one
},
}
I have one function in Redux actions that load all these data.
But now I need something like a separate function updateData function which will be updating one value in an existing state object without having to refresh everything.
Is it possible to do so in Redux?

Your state is an object (not an array) of objects.
You can't update a single property of your state. You have to supply a new object with the updated values. Study immutability in redux: https://redux.js.org/faq/immutable-data
Try this in your reducer:
const changedVal=state[2];
changedVal.state='ON';
return {...state,changedVal};

You will need to familiarise yourself with how actions and reducers work in Redux but yes, this is most definitely possible.
You can make an UPDATE_DATA action creator that you would call inside your react component(s). And you would have a reducer that will control how the data is processed and state changed.
-edit-
As mentioned in the comments, I use "immer" in reducers with Redux. Here's an example of how I've used it for editing and existing item in an array:
case EDIT_ITEM:
return produce(state, (draft) => {
const index = findItemIndex(draft, action.payload._id)
draft[index] = action.payload
})
So you pass in state and a callback, with draft being the mutable equivalent of the state object.

In short, updating one property isn't possible practically. Of course it can't be done, but it's very difficult. I can explain why.
Let's start with updating an item. This is definitely possible. Suppose you have a redux selector listening to the data, when sending the data over to a component.
const DisplayOneItem = React.memo(( name, age ) => { ... })
You can see, first the name (and age) is sent to this component, also a React.memo is applied so that only when one of the property is changed, it can then update. But don't get mixed with the following line:
const DisplayOneItem = React.memo(( item ) => { ... })
The above line takes entire item over, and when you change one property, the item changes as well, therefore no matter what, you will get a new update.
You might wonder what happens if React.memo isn't used? Yes, render all the time. Practically if you change a property, either item or data gets changed entirely. Otherwise how do you notify React there's a change? :)
NOTE: React can't update according to a value change by default. The reason why it updates is because you ask it to, through setState({ ...data }). So now it's the question about how granularity YOU control the dispatch (or render). People might think React is magic in terms of rendering, but actually it only does it upon a request. So now your question is, I ask it to render, but it shouldn't render? Haha, you get the point.

Related

Alternative way to handle state in React using memo

When it comes to handling complex state in React everybody suggests to flatten the state to avoid something like this just to update a single property:
setState({ …state, user:{ …state.user, profile:{…state.user.profile, address:{…state.user.profile.address, city:’Newyork’}} }});
Which is really cubersome to work with. There is another way: use an object holding your state and return that from a memoized function. Then whenever you made a change simply force a re-render.
// note the reference cannot be changed, but values can.
const data = useMemo(() => ({
user: {
name: "",
profile: {
address: {
city: "New york"
}
}
}
}), []);
// use dummy data to trigger an update
const [toggle, setToggle] = useState(false);
function forceUpdate() {
setToggle(prev => !prev);
}
function makeChanges() {
// make any change on data without any copying.
data.user.address.city = "new city name";
// hydrate the changes to the view when you're done
forceUpdate();
}
return (
<div onClick={() => makeChanges()}>{data.user.address.city }</div>
)
Which works perfectly. Even with massive and complex data structures.
From what I can tell state is really just a memoized values which will trigger an update upon change.
So, my one question: What is the downside of using this?
The docs say useMemo is not a guarantee:
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values [...]
If you'd really want to do something like this and you are absolutely positively sure you're willing to do things unlike anyone else in React land, you'd use useRef for state storage that doesn't cause rerenders by itself. I'm not going to add an example of that, because I don't recommend it in the least.
You should also note that your method will not cause memoized (React.memo()) components to rerender, since they will not "see" changes to props if their identity does not change. Similarly, if another component uses one of your internally mutated objects as a dependency for e.g. an effect, those effects will not fire. Finding bugs caused by that will be spectacularly annoying.
If modifying deep object structures is otherwise cumbersome, see e.g. the Immer library, which does Proxy magic internally to let you modify deep objects without trouble – or maybe immutability-helper if you're feeling more old-school.

How to save selected options while going back ReactJS?

I have 2 useEffect hooks for fetching all cars, then user can select some options, and component will rerendered with new fetched data, considering selected options, further user can navigate to some component and after that go back and all data will be fetched with initial state (all selected options will be erased). How to save selected options and use it for next renderings ? Furthermore It would be better to use only one hook, but i was confused in logic.
useEffect(() => {
fetchManufacturers().then(data => car.setManufacturers(data))
fetchCarNames().then(data => car.setCarNames(data))
fetchCars( null, null, 1, car.limit).then(data => {
car.setCars(data.rows)
car.setTotalCount(data.count)
})
},[car, car.page, car.selectedManufacturer, car.selectedCarName])
useEffect(() => {
fetchCars(car.selectedManufacturer.id , car.selectedCarName.id, car.page, car.limit).then(data => {
car.setCars(data.rows)
car.setTotalCount(data.count)
})
}, [car.page, car.selectedManufacturer, car.selectedCarName])
I tried to use getters and setters, but it save the 1 step back selected option.
You could either store your data in a parent component, but I suggest storing it in a context. Since it is related to a user decision, it could be interesting to get these data from wherever you want.
You can find a simple example on how to use a the React contextApi in my answer to another post.
There is other state management, but I like this one for its simplicity.
React Redux is great for what you're trying to do. You can fetch all the raw car data once and save it in Redux. Every component can then fetch that data as needed. You can even store your user's car selections in Redux if you need to share that state between components.

want to know the disadvantages of using state (this.state) react components (not redux)

i just want to know this.whether state json object binding and normal json object binding is same or not? below is the example.
1st example
state = { name: "Default", email: "" };
binding data : this.state.name
this.setState({ name: e.currentTarget.value })
2nd example
const data= {name: "Default", email: ""}
binding data to control: data.name
onchange={e=>data.name=e.value}
both are working fine but want to know which one is better in performance?
my application dosent need any imutable data because i not displaying data dynamically i need to fetch the data from api on component load and posting the data to api on form submit. so i am using the 2nd approch. where i feel state will unnecessarly load render object.
so can any one suggest which one is better?
If the state is not associated with any UI component then changing it will not re-render anything so functionally both will work the same.
In terms of performance 2nd approach will be faster as it is a direct object manipulation whereas calling setState is a function call and doesn't guarantee immediate execution.
However do not use 2nd approach at all because it will create confusion for the next developer who manages such code(In my opinion). In the long run when the data grows you will have to keep a separate state obj for managing the data and the UI.
So it is always better to keep them separate from the beginning.

React: setting state and default input from prop

I'm creating a app that allows users to create assessments (that other users can complete). Right now I am working on an 'edit' page, where a users loads a form that is prefilled with the relevant data - but he is then able to change these values.
However, I am having trouble with two things (that I suspect are related).
First: the input fields wont display a default value that is derived from the component state.
Second: If I set the input fields directly from the props I am no longer able to change the values.
The components gets passed a prop that is called block_data which is a dict containing key/value pairs of strings.
I'm attempting to convert load it into the state like so
constructor(props) {
super(props);
this.state = {
block: this.props.block_data,
question_list: [],
question_options: ['BO', 'SC', 'SR', 'NO'],
block_list: [],
};
(..)
}
However, this does not work. When I check in the chrome react extension the state is not set.
The input fields are all very simular to the example I've included below. Here I've set its value from the props. In this case it does display the correct initial data. But I am unable to edit it.
<input
onChange={e => this.changeIsANaturalPartOfLife(e)}
value={this.props.block_data.title}
name="title"
/>
Below is the 'on change' function. When I check the chrome react tool, I can see that only the first letter of the state is updated when I start typing. The input field does not show any changes.
changeIsANaturalPartOfLife(e, optional) {
const target = e.target;
const name = target.name;
const value = target.value;
console.log(value);
this.setState({ block: {[name]: value }});
}
I am really at a loss here on what to do. What I am trying to do here seems simple enough, yet I'm unable to advance beyond this flawed stage. Does anyone have an idea what I am doing wrong?
As you mentioned in comment: "the data is loaded from a DjangoRestAPI".
Solution of first problem:
You need to use componentwillreceiveprops lifecycle method to update the state with new props values (after successfully fetched from server) otherwise state of child component will always have the initial data of parent component.
As per DOC:
componentWillReceiveProps() is invoked before a mounted component
receives new props. If you need to update the state in response to
prop changes (for example, to reset it), you may compare this.props
and nextProps and perform state transitions using this.setState() in
this method.
Use this method:
componentwillreceiveprops(nextProps) {
// compare nextProps.block_data and this.state.block if not equal
this.setState({
block: nextProps.block_data
})
}
Solution of second problem:
When you are directly using the props instead of storing into state, then you need to update the value in parent component during onChange, because you are using this.props.value and updating this.state.value, and hence props.value will always be same and it will not allow you to type anything.

Avoid re-rendering a big list of items with react-redux

I am using redux with react and typescript for my application. I am working with many items used at different places of my app. My state looks like this:
{
items: {42: {}, 53: {}, ... }, //A large dictionary of items
itemPage1: {
itemsId: [ 42, 34, 4 ],
...
},
itemPage2: { ...
},
...
}
The user can modify some attributes of the items dispatching some actions. When this happen I need to redraw the components that have been modified in each pages. The issue is that my items are quite big and I cant afford to redraw all of them at each small modification. I was wondering is this approach would work:
I have a fist component <ItemPage1> which connects to the store to get all of the states stored in the tree under itemPage1 e.g. the list of items id: itemsId.
Inside <ItemPage1>, I loop over the itemsId property to generate multiple FilterItem components: itemsId.map( itemId => return <FilterItem id=itemId>);
Finally each Item is connected using ownProps to get the correct part of the state:
const mapStateToItemProps = (state, ownProps) => {
return {
item: state.items[ownProps.id],
}
}
const mapDispatchToItemProps = (dispatch, ownProps) => {
return null;
}
const FilterItem = connect(
mapStateToItemProps,
mapDispatchToItemProps
)(Item)
Can you confirm or refute that if I update the item of id 42, then only this item is going to be re-rendered ?
When rendering big list you need to take into considerations few things :
Lower the total number of DOM elements that you need to render (by not rendering items that are not actually visible on the screen, also known as virtualization)
Don't re-render items that have not changed
Basically, what you want to avoid is a complete re-render of your list (or your page) when the user edits one single row. This can be achieved exactly how you did it, i.e : by passing to the list container only the ids of items that need to be rendered, and to map over these ids to connect each component by using ownProps. If you have a dump <Item/> component, your <ItemPage/> component will create connected connect(<Item/>) component.
This is going to work, if your put a console.log('item rendered') in your <Item/> component class you will notice that there is only one call.
BUT (and it's a big but), what is not obvious when working with react-redux is that all connected components that depends on their ownProps will always rerender if any part of the state change. In your case, even if the <Item/> components will not re-render, their wrapped component connect(Item) will ! If you have few dozens of items, you might encounter some latency if actions need to be dispatched quickly (for example when typing in an input). How to avoid that ? Use a factory function to use ownProps as the initial props :
const mapStateToItemProps = (_, initialProps) => (state) => {
return {
item: state.items[initialProps.id], // we're not relying on the second parameters "ownProps" here, so the wrapper component will not rerender
}
}
const mapDispatchToItemProps = (dispatch, ownProps) => {
return null;
}
const FilterItem = connect(
mapStateToItemProps,
mapDispatchToItemProps
)(Item)
I suggest you to take a look to this other answer.
You might also be interested in these excellent slides : Big List High Performance React & Redux
And finally, you should definitively take a look to react-virtualized to perform the virtualization of your list (i.e, displaying only the item that the user can actually see).
Ok, I've found this discussion: https://github.com/reactjs/redux/issues/1303
At the bottom it is clearly stated (from multiple protagonists):
[...] react-redux takes care of this. It lets you specify specific parts of the state you care about, and takes care to bail out of updating React components when the relevant parts have not changed.
[...] Just wanted to fully understand that what's going on under the hood here, So if the Redux store gets updated but one specific component state hasn't changed, Redux won't trigger the forceUpdate() method for that component? [...]
The wrapper component generated by React-Redux's connect() function does a several checks to try to minimize the number of times your actual component has to re-render. This includes a default implementation of shouldComponentUpdate, and doing shallow equality checks on the props going into your component (including what's returned from mapStateToProps). So yes, as a general rule a connected component will only re-render when the values it's extracting from state have changed.
So I believe my implementation is good, it won't re-render all the items since only one item will see its properties modified.

Resources