how to avoid mutating this.state or redux object - javascript

For example, if I want to do any operation on this.state or some other object, such as Object.keys(this.state).map() to get access to all the values in the object.
Is it bad practice to do this?
Object.keys(this.state).map(k => this.state[k]);
Thanks

In React, you should only mutate this.state using this.setState() so that React knows when to re-render components.
The line of code you've written is fine, because you're not actually modifying the state object. You've already created a new array using Object.keys.
If however, you want to copy this.state without modifying it, try the following.
const state = {...this.state};
// Or
const state = Object.assign({}, this.state);

React Mutating State.
let state = {
age : 25,
name : 'Robin',
friends: ['Barney', 'Ted']
}
Change Age
this.setState({age: 26})
Change Name
this.setState({name: 'Lily'})
Add Friend
this.setState({
friends: this.state.friends.concat(['Marshall'])
})
Try this library immutability-helper for complex state updates.

Related

Cannot update the state of an object within the state (react)

I have an object variable within the state of my react app. I initialize it with the structure that I want the object to have. Late I am trying to update that object using the setState function. My issue is, I can't get it to actually update anything within the state. The way my code currently looks is:
// the initial state
var initialState = {
object: {
foo: '',
bar: 0,
array: [<an array of objects>]
}
};
// in componentDidMount
this.state = initialState;
// updating the object that is within the state (also in componentDidMount)
let object = {
foo: 'Value',
bar: 90,
array: [<a populated array of objects>]
};
this.setState(prevState => ({
...prevState.object,
...object
}));
console.log(this.state.object); // returns the object defined in initial state
I am really not sure how to fix this issue. I have also been trying a few other methods (especially the ones outlined in this post: Updating an object with setState in React
I have not tried every method outlined in that post but the ones I have been trying have not been working and I figure that this means it is a mistake that does not involve which method I am using. Any insight into this issue would be greatly appreciated. I tried to make this code as concise as possible but if you want to see the exact code I am using (which is pretty much just this but more) just ask.
Edit 2
You have to care each object key equality.
You can do this.
this.setState((prevState) => {
console.log('logging state: ', prevState)
return {
...prevState.object,
object:{ ...object }
})
)}

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.

React Best Practices: Having properties other than `state` in a component

I'm reviewing a React Component and it contains a state property as well as an allData property.
To follow best practice, shouldn't allData be a part of state?
constructor(props) {
super(props);
this.allData = [];
this.state = {
allDisplayedData: [],
allRowsCount: -1,
favData: [],
favRowsCount: -1,
};
this.searchAll = this.searchAll.bind(this);
this.handleCellClick = this.handleCellClick.bind(this);
}
If you want the component to re-render on changing it, then it needs to be in state, otherwise put it wherever. Any instance variables other than this.state aren't part of React's control, so they don't have the same ability to set using setState. This means that they don't re-render the component like the state does.
Essentially, it depends on what you do with it and how you want to work with it.
I tend to use this pattern for things like cancelTokens and intervalIds and other data I might need later, but don't need as part of the state because it's only needed in unmount or update but not in the render itself.
If it's needed in the render, you should have it in state or be prepared to deal with the component not rendering when it's updated.
If you want to make the array part of state, then yes, if no, then no.
Other than the two previous detailed answers, I found the following statement at https://reactjs.org/docs/state-and-lifecycle.html
While this.props is set up by React itself and this.state has a
special meaning, you are free to add additional fields to the class
manually if you need to store something that doesn’t participate in
the data flow (like a timer ID).

MapStateToProps to component state in an sync context

I know we can easily send the content of mapStateToProps in the component's state by doing so :
constructor(props){
super(props);
this.state = {
filteredApps: this.props.apps
}
}
In this usecase, this.state.filteredApps gets filled with what was mapped to props from Redux.
But what if this.props.apps is only filled properly after an async call? In an async context, this.props.apps will probably be an empty array for when it is initialized until the real data is fetched. Take this as an example :
class AppFilterer extends React.Component {
constructor(props) {
super(props);
this.state = {
filteredApps : this.props.apps
}
}
componentWillMount() {
this.props.getApps();
}
render(){ return <div> </div> }
}
const mapStateToProps = state => {
let { apps } = state.Admin;
return { apps };
};
export default connect(mapStateToProps, { getApps })(AppFilterer);
In this case, my Redux action (which is caught by an Saga) this.props.getApps(); is the call that fills my props full of apps and is called from the componentWillMount function. It is initialized as an empty array and then gets filled with apps once the call is complete.
I wish to filter these apps once they are fetched from the API so want to put them inside my component's state so that I don't mess with the Redux state. What is the best practice for updating the component's state in this case? In other words, is there any way to take the result of a saga that has been mapped to props and set it into the component's state or am I looking for a weird pattern and should filter it some other way?
First of all API calls go in componentDidMount not in componentWillMount which is also now deprecated. Please refer this guide:
https://reactjs.org/docs/react-component.html
Secondly, when you are using redux state and mapping it to props, you should not set that in your component local state, that’s not a good practice. You’ll receive updated props when your promise will return and you can always rely on props in that scenario.
But if you still want to do that you can override componentDidUpdate(prevProps) which will be called when your props or state is updated. Here is where you can set your state if you still want to do that.
Note for your filter thing
You can do filtering in componentDidUpdate method like:
this.setState({filteredApps. this.props.apps.filter(<your filter logic>)})

Referencing state from within state - React js

Is it possible to reference state from within state to create an id. e.g id = this.state.name + this.state.desc
Something like this?
this.state = {name:'', desc:'', id:`${this.state.name}-${this.state.desc}`}
Nope, that won't work. this.state is initialized before the constructor, but it won't like that you are referencing an undefined portion of the object. You would need to set it after initialization. I'm assuming you would be using this.state = {blah} from your constructor. Otherwise, you should use this.setState().
However this is also bad practice because anytime you update your name or desc, it will update the state again with your new id value. I don't know the full scope of your code, but you can probably just save the id to a function that gives you the string.
Remember you need to set the state using setState
So
this.setState({ id:`${this.state.name}-${this.state.desc}` })
This is also assuming that you have already set the state in the constructor (or somewhere else int he app).
If you are in the constructor, then this should work just fine...
this.state = { name: 'Toli', desc: 'A programmer' };
this.state = {...this.state, id:`${this.state.name}-${this.state.desc}`}
I'm assuming you want to keep name and desc in the state so I'm copying them using the spread op (...this.state)

Categories