Using class property to set initial state in React - javascript

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.

Related

What is the equivalent of 'this.state' with the new react useState hook?

I am practicing the new React hooks and I was wondering if anyone figured out what the equivalent is of console.log(this.state) in terms of the useState hook in a function component.
Regards!
There are no 'state' in a function component.
You could have a state object by using 'useReducer' instead. (if your state is a complex object)
const [state, dispatch] = useReducer(reducer, initialState);
If you are converting a React App from class based to hooks.
look for something like this
class App extends Component {
constructor(props) {
super(props);
this.state = {
var1: "foo",
var2:"bar"
};
}
in this case
this.state.var1 = "foo"
this.state.var2 = "bar"
this.state =
{
var1: "foo",
var2:"bar"
}

React Component changing from function to class - State and state hook

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

Adding methods to react context

So I have an app that looks like this (tried to simplify as much as possible):
// file with context
const SomeContext = React.createContext({});
// file with app root
class App extends Component {
constructor() {
super();
this.state = {
someValue: 123
};
}
doSomething() {
// this does not work,
// as "this" is context value
this.setState({
someValue: someValue + 1
});
}
render() {
// create context values
const value = {
someValue: this.state.someValue
doSomething: this.doSomething
};
return <SomeContext.Provider value={value}><ChildComponent/></SomeContext>;
}
}
// file with child component
class ChildComponent extends Component {
render() {
return <button onClick={this.context.doSomething} />
}
}
ChildComponent.contextType = SomeContext;
I want context to have doSomething method that updates state of the app (specifically increase someValue by 1).
How to correctly execute doSomething method from child component?
Do I have to do doSomething: this.doSomething.bind(this)? or there is a better way?
edit question is less about how to get "this", but more about what's the recommended way to do it in this case. Maybe I should not add method to context at all?

Clearing state es6 React

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

Reactjs, parent component, state and props

I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.

Categories

Resources