I am trying to clear a components state but can't find a reference for the es6 syntax. I was using:
this.replaceState(this.getInitialState());
however this does not work with the es6 class syntax.
How would I achieve the same result?
To the best of my knowledge, React components don't keep a copy of the initial state, so you'll just have to do it yourself.
const initialState = {
/* etc */
};
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = initialState;
}
reset() {
this.setState(initialState);
}
/* etc */
}
Beware that the line this.state = initialState; requires you never to mutate your state, otherwise you'll pollute initialState and make a reset impossible. If you can't avoid mutations, then you'll need to create a copy of initialState in the constructor. (Or make initialState a function, as per getInitialState().)
Finally, I'd recommend you use setState() and not replaceState().
Problem
The accepted answer:
const initialState = {
/* etc */
};
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = initialState;
}
reset() {
this.setState(initialState);
}
/* etc */
}
unfortunately is not correct.
initialState is passed as a reference to this.state, so whenever you change state you also change initialState (const doesn't really matter here). The result is that you can never go back to initialState.
Solution
You have to deep copy initialState to state, then it will work. Either write a deep copy function yourself or use some existing module like this.
This is the solution implemented as a function:
Class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
getInitialState = () => ({
/* state props */
})
resetState = () => {
this.setState(this.getInitialState());
}
}
The solutions that involve setting this.state directly aren't working for me in React 16, so here is what I did to reset each key:
const initialState = { example: 'example' }
...
constructor() {
super()
this.state = initialState
}
...
reset() {
const keys = Object.keys(this.state)
const stateReset = keys.reduce((acc, v) => ({ ...acc, [v]: undefined }), {})
this.setState({ ...stateReset, ...initialState })
}
First, you'll need to store your initial state when using the componentWillMount() function from the component lifecycle:
componentWillMount() {
this.initialState = this.state
}
This stores your initial state and can be used to reset the state whenever you need by calling
this.setState(this.initialState)
const initialState = {
a: '',
b: '',
c: ''
};
class ExampleComponent extends Component {
state = { ...initialState } // use spread operator to avoid mutation
handleReset = this.handleReset.bind(this);
handleReset() {
this.setState(initialState);
}
}
Remember that in order to be able to reset the state it is important not to mutate initialState.
state = {...initialState} // GOOD
// => state points to a new obj in memory which has the values of initialState
state = initialState // BAD
// => they point to the same obj in memory
The most convenient way would be to use ES6 Spread Operator. But you could also use Object.assign instead. They would both achieve the same.
state = Object.assign({}, initialState); // GOOD
state = {...initialState}; // GOOD
In most cases you dont need a deep copy, rarely initial state is object of objects, so using spread operator which babel transpiles to the object.assign should be fine.
So, inside constructor you would have:
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
key: value,
key2: value
}
this.initialState = { ...this.state }
}
}
From there you can use
this.setState(this.initialState);
to reset. But if for some reason your initial state is more complex object, use some library.
This is how I handle it:
class MyComponent extends React.Component{
constructor(props){
super(props);
this._initState = {
a: 1,
b: 2
}
this.state = this._initState;
}
_resetState(){
this.setState(this._initState);
}
}
Update:
Actually this is wrong. For future readers please refer to #RaptoX answer.
Also, you can use an Immutable library in order to prevent weird state modification caused by reference assignation.
I will add to the above answer that the reset function should also assign state like so:
reset() {
this.state = initialState;
this.setState(initialState);
}
The reason being that if your state picks up a property that wasn't in the initial state, that key/value won't be cleared out, as setState just updates existing keys. Assignment is not enough to get the component to re-render, so include the setState call as well -- you could even get away with this.setState({}) after the assignment.
In some circumstances, it's sufficient to just set all values of state to null.
If you're state is updated in such a way, that you don't know what might be in there, you might want to use
this.setState(Object.assign(...Object.keys(this.state).map(k => ({[k]: null}))))
Which will change the state as follows
{foo: 1, bar: 2, spam: "whatever"} > {foo: null, bar: null, spam: null}
Not a solution in all cases, but works well for me.
You can clone your state object, loop through each one, and set it to undefined. This method is not as good as the accepted answer, though.
const clonedState = this.state;
const keys = Object.keys(clonedState);
keys.forEach(key => (clonedState[key] = undefined));
this.setState(clonedState);
class MyComponent extends Component {
constructor(props){
super(props)
this.state = {
inputVal: props.inputValue
}
// preserve the initial state in a new object
this.baseState = this.state
}
resetForm = () => {
this.setState(this.baseState)
}
}
use deep copy, you can do it with lodash:
import _ from "lodash";
const INITIAL_STATE = {};
constructor(props) {
super(props);
this.state = _.cloneDeep(INITIAL_STATE);
}
reset() {
this.setState(_.cloneDeep(INITIAL_STATE));
}
class x extends Components {
constructor() {
super();
this.state = {
name: 'mark',
age: 32,
isAdmin: true,
hits: 0,
// since this.state is an object
// simply add a method..
resetSelectively() {
//i don't want to reset both name and age
// THIS IS FOR TRANSPARENCY. You don't need to code for name and age
// it will assume the values in default..
// this.name = this.name; //which means the current state.
// this.age = this.age;
// do reset isAdmin and hits(suppose this.state.hits is 100 now)
isAdmin = false;
hits = 0;
}// resetSelectively..
}//constructor..
/* now from any function i can just call */
myfunction() {
/**
* this function code..
*/
resetValues();
}// myfunction..
resetValues() {
this.state.resetSelectively();
}//resetValues
/////
//finally you can reset the values in constructor selectively at any point
...rest of the class..
}//class
Related
I've got a question, because i can't find answer anywhere.
The problem is: what is the initial value of the following hook:
"Function" Example:
function Example() {
const [settings, setSettings] = useState({});
return (
...
);
}
And here i'm writing this function "Example" as class, but i don't know how to initialize state
(is it empty string or empty list or 0 value?)
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: <-- and what here?
};
}
useState is passed the initial value as its only param - in your case {}.
You can accomplish the same in your second code snippet like so:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: {}
};
}
}
Further reading:
The React docs have an example almost identical to yours.
useState source
I have a React class component that with an initial state object in the constructor call. I originally just had an object literal assigned to this.state, but I need to share the initial state object with other methods in the class to reset the component. Is it ok/correct to move the initial state object to be a class property and reference that in the constructor?
class SampleComponent extends Component {
constructor() {
super();
this.state = this.initialState;
}
initialState = {
property1: "...",
property2: "..."
};
}
The code seems to work, but I'm not sure if this is the right way to go about this.
Decouple the initialState from the class:
const initialState = {
property1: "...",
property2: "..."
};
// As Class
class SampleComponent extends Component {
state = initialState;
...
}
// Hooks
function SampleComponent(props) {
const [myState, setMyState] = useState(initialState);
...
}
In this way, you avoid future bugs regarding this.initialState.
I'm using reactjs and there is a instantiation in componentDidUpdate(). But when update state, the componentDidUpdate() will be execute and the Object will repeat statement
ps: const barrage = new Barrage(); the object will be execute repeatly
componentDidUpdate() {
const barrage = new Barrage();
}
const barrage = new Barrage();
how to avoid the Object statement repeatly execute
you haven't really explained what you want to achieve but I will try to solve this specific question by saying you can put it in a state if you need it to create new instance only once
constructor(props) {
super(props);
...
this.state = {
barrage : null
}
...
}
componentDidUpdate(prevState) {
if(!prevState.barrage) {
this.setState({ barrage : new Barrage() )}
}
}
You could create it in componentDidMount and cleanup in componentWillUnmount
class TestComponent extends React.Component {
barage = null;
componentDidMount() {
this.barage = new Barage();
}
componentWillUnmount() {
this.barage = null;
}
}
It's really hard to guess what you're doing with that object without any code. I can only assume that it is independent of the updates to the component, if any.
Currently I'm using the following:
const mapStateToProps = state => {
return {
stateInProps: state.someSliceIWant
};
};
Foo.propTypes = {
dispatch: PropTypes.func,
stateInProps: PropTypes.object
};
export default connect(mapStateToProps)(Foo);
and since we can only actually map state to props, and not directly to state in my component, I'm getting around this in a hacky way by doing this:
class Foo extends React.Component {
constructor(props){
super(props);
this.state = {};
setTimeout(() => {this.setState(this.props.stateInProps);},1);
}
How should I do this properly? I've tried using different lifecycle hooks and all sorts of methods.
An interesting thing I found was that if I put setState() in mapStateToProps.... it ALMOST works.
const mapStateToProps = state => {
Foo.setState(state.someSliceIWant);
return {
stateInProps: state.someSliceIWant
};
};
It throws a bunch of errors, but when I delete that line and webpack grinds through and the page re-renders, the state has actually been updated properly and the values are there! WHAT?! I can't figure out how to get it do that, without throwing the errors, as Redux/React itself won't allow it as best I can tell.
Have you tried ComponentWillReceiveProps after mapping state to Props?
For instance:
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
The hacky way you are using, you might not able to access your stateInProps if it get updated in later lifecycle.
You can use something as below
class Foo extends React.Component {
state = {};
componentWillMount() { this.setStateInProps(this.props); }
componentWillReceiveProps(nextProps) { this.setStateInProps(nextProps)}
setStateInProps= (props) => this.setState(props.stateInProps)
}
I have a single state inside my React component which has the structure like this
{a: {x: 0}, b: {x:0}}. During the operation time, i need to trigger several requests to server in order to request the data in order to update the state, and this need to be updated in the state. Since the number of the record in the database is quite big, I have to trigger several times
If I do like this
request_data(e) {
['a', 'b'].forEach((k) => {
// do not mutate the state directly
let new_state = _.extend({}, state);
request(params, (err, res) => {
// set result to the new_state
new_state = res;
// update the original state
this.setState(newState);
})
});
}
inside the callback, the second request will not have the data of the first request the moment it request and update the origin state with the empty value for the first branch of the state structure.
One simple solution is updating the origin state directly, but I don't think it is a good idea. I am planning to integrate redux later but at this moment, since the current code base is quite big, migrating gradually is better. However, I dont' want to alter the origin state directly since it is not the redux way. Any suggestion to overcome this issue ?
I'm assuming by the way your code is written, you're using es6 classes for your component? That being the case, hopefully this tidbit will help:
import React from 'react'
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
foo:'bar'
}
}
request_data = (e) => {
['a', 'b'].forEach((k) => {
// do not mutate the state directly
let new_state = _.extend({}, state);
request(params, (err, res) => {
// set result to the new_state
// update the original state
this.setState(foo:res.foo)
})
});
}
render() {
return (
// component code here
)
}
}
export default MyComponent
Notice that I'm using an arrow function for the request_data part...this is to avoid need to bind the function to the this variable inside of the constructor.
UPDATE:
I'm still not quite sure I understand what problem you're having here...but if you're not wanting to use redux right now, you could go with a container component method. Basically, your container component's main job is to manage the data and do nothing but pass its updated state to a child component as props. Something like this:
class MyComponentContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
a: {
x: 0
},
b: {
x:0
}
}
}
componentWillMount() {
['a', 'b'].forEach((k) => {
request(params, (err, res) => {
this.setState({
a: res.a,
b: res.b
});
})
});
}
render() {
return (
<MyComponent data={this.state} />
)
}
}
class MyComponent extends React.Component {
constructor (props) {
super(props)
}
render() {
return (
<div>
{/* display stuff here */}
</div>
)
}
}
export default MyComponentContainer