I am studying the principles of react.
According to some reviews, some people says is better to keep your component stateless, what does it mean?
But other people says, that if you need to update your component, then you should learn how to set your state to the proper state.
I saw this.props / this.setProps and this.state / this.setState and I am confuse with that.
Something I am trying to figure is, how can I update a component by itself and not from a parent component? should I use props or state in this case?
I already read some docs about props and state, what I don't have clear, is: when to use one or another ?
Props vs. state comes down to "who owns this data?"
If data is managed by one component, but another component needs access to that data, you'd pass the data from the one component to the other component via props.
If a component manages the data itself, it should use state and setState to manage it.
So the answer to
how can I update a component by itself and not from a parent component? should I use props or state in this case?
is to use state.
Props should be considered immutable and should never be changed via mutation. setProps is only useful on a top-level component and generally should not be used at all. If a component passes another component a property, and the first component wants the second to be able to change it, it should also pass it a function property that the second component can call to ask the first component to update its state. For example:
var ComponentA = React.createClass({
getInitialState: function() {
return { count: 0 };
},
render: function() {
return <Clicker count={this.state.count} incrementCount={this.increment} />;
},
increment: function() {
this.setState({count: this.state.count + 1});
}
});
// Notice that Clicker is stateless! It's only job is to
// (1) render its `count` prop, and (2) call its
// `incrementCount` prop when the button is clicked.
var Clicker = React.createClass({
render: function() {
// clicker knows nothing about *how* to update the count
// only that it got passed a function that will do it for it
return (
<div>
Count: {this.props.count}
<button onClick={this.props.incrementCount}>+1</button>
</div>
);
}
});
(Working example: https://jsbin.com/rakate/edit?html,js,output)
For and object-oriented programming analogy, think of a class/object: state would be the properties you put on the class; the class is free to update those as it sees fit. Props would be like arguments to methods; you should never mutate arguments passed to you.
Keeping a component "stateless" means that it doesn't have any state, and all its rendering is based on its props. Of course, there has to be state somewhere or else your app won't do anything! So this guideline is basically saying to keep as many components as possible stateless, and only manage the state in as few top-level components as possible.
Keeping components stateless makes them easier to understand, reuse, and test.
See A brief interlude: props vs state in the React docs for more information.
Use state when you know the variable value is going to affect the view. This is particularly critical in react, because whenever the state variable changes there is a rerender(though this is optimized with the virtual DOM, you should minimize it if you can), but not when a prop is changed (You can force this, but not really needed).
You can use props for holding all other variables, which you think can be passed into the component during the component creation.
If you have want to make a multi-select dropdown called MyDropdown for example
state = {
show: true,
selected:[],
suggestions:this.props.suggestionArr.filter((i)=>{
return this.state.suggestions.indexOf(i)<0;
})
}
props={
eventNamespace:'mydropdown',
prefix : 'm_',
suggestionArr:[],
onItemSelect:aCallbackFn
}
As you can see, the objects in the state variable are going to affect the view some way or the other.
The objects in the props are mostly objects which should remain the same throughout the component life cycle. So these objects can be callback functions, strings used to namespace events or other holders.
So if you do want to update the component by itself, you need to have to look into how componentWillRecieveProps ,componentWillUpdate, componentDidUpdate and componentShouldUpdate works. More or less, this depends on the requirement and you can use these lifecycle methods to ensure that the rendering is within the component and not in the parent.
Related
I understand that React tutorials and documentation warn in no uncertain terms that state should not be directly mutated and that everything should go through setState.
I would like to understand why, exactly, I can't just directly change state and then (in the same function) call this.setState({}) just to trigger the render.
E.g.: The below code seems to work just fine:
const React = require('react');
const App = React.createClass({
getInitialState: function() {
return {
some: {
rather: {
deeply: {
embedded: {
stuff: 1,
},
},
},
},
},
};
updateCounter: function () {
this.state.some.rather.deeply.embedded.stuff++;
this.setState({}); // just to trigger the render ...
},
render: function() {
return (
<div>
Counter value: {this.state.some.rather.deeply.embedded.stuff}
<br></br>
<button onClick={this.updateCounter}>Increment</button>
</div>
);
},
});
export default App;
I am all for following conventions but I would like to enhance my further understanding of how ReactJS actually works and what can go wrong or is it sub-optimal with the above code.
The notes under the this.setState documentation basically identify two gotchas:
That if you mutate state directly and then subsequently call this.setState this may replace (overwrite?) the mutation you made. I don't see how this can happen in the above code.
That setState may mutate this.state effectively in an asynchronous / deferred way and so when accessing this.state right after calling this.setState you are not guaranteed to access the final mutated state. I get that, by this is not an issue if this.setState is the last call of the update function.
This answer is to provide enough information to not change/mutate the state directly in React.
React follows Unidirectional Data Flow. Meaning, the data flow inside react should and will be expected to be in a circular path.
React's Data flow without flux
To make React work like this, developers made React similar to functional programming. The rule of thumb of functional programming is immutability. Let me explain it loud and clear.
How does the unidirectional flow works?
states are a data store which contains the data of a component.
The view of a component renders based on the state.
When the view needs to change something on the screen, that value should be supplied from the store.
To make this happen, React provides setState() function which takes in an object of new states and does a compare and merge(similar to object.assign()) over the previous state and adds the new state to the state data store.
Whenever the data in the state store changes, react will trigger an re-render with the new state which the view consumes and shows it on the screen.
This cycle will continue throughout the component's lifetime.
If you see the above steps, it clearly shows a lot of things are happening behind when you change the state. So, when you mutate the state directly and call setState() with an empty object. The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. This will disrupt all the React's Lifecycle Methods.
As a result, your app will behave abnormal or even crash. Most of the times, it won't affect your app because all the apps which we use for testing this are pretty small.
And another downside of mutation of Objects and Arrays in JavaScript is, when you assign an object or an array, you're just making a reference of that object or that array. When you mutate them, all the reference to that object or that array will be affected. React handles this in a intelligent way in the background and simply give us an API to make it work.
Most common errors done when handling states in React
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// bad approach
const b = this.state.a.push(6)
this.setState({
a: b
})
In the above example, this.state.a.push(6) will mutate the state directly. Assigning it to another variable and calling setState is same as what's shown below. As we mutated the state anyway, there's no point assigning it to another variable and calling setState with that variable.
// same as
this.state.a.push(6)
this.setState({})
Many people do this. This is so wrong. This breaks the beauty of React and is bad programming practice.
So, what's the best way to handle states in React? Let me explain.
When you need to change 'something' in the existing state, first get a copy of that 'something' from the current state.
// original state
this.state = {
a: [1,2,3,4,5]
}
// changing the state in react
// need to add '6' in the array
// create a copy of this.state.a
// you can use ES6's destructuring or loadash's _.clone()
const currentStateCopy = [...this.state.a]
Now, mutating currentStateCopy won't mutate the original state. Do operations over currentStateCopy and set it as the new state using setState().
currentStateCopy.push(6)
this.setState({
a: currentStateCopy
})
This is beautiful, right?
By doing this, all the references of this.state.a won't get affected until we use setState. This gives you control over your code and this'll help you write elegant test and make you confident about the performance of the code in production.
To answer your question,
Why can't I directly modify a component's state?
Well, you can. But, you need to face the following consequences.
When you scale, you'll be writing unmanageable code.
You'll lose control of state across components.
Instead of using React, you'll be writing custom codes over React.
Immutability is not a necessity because JavaScript is single threaded, but it's a good to follow practices which will help you in the long run.
PS. I've written about 10000 lines of mutable React JS code. If it breaks now, I don't know where to look into because all the values are mutated somewhere. When I realized this, I started writing immutable code. Trust me! That's the best thing you can do it to a product or an app.
The React docs for setState have this to say:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Basically, if you modify this.state directly, you create a situation where those modifications might get overwritten.
Related to your extended questions 1) and 2), setState() is not immediate. It queues a state transition based on what it thinks is going on which may not include the direct changes to this.state. Since it's queued rather than applied immediately, it's entirely possible that something is modified in between such that your direct changes get overwritten.
If nothing else, you might be better off just considering that not directly modifying this.state can be seen as good practice. You may know personally that your code interacts with React in such a way that these over-writes or other issues can't happen but you're creating a situation where other developers or future updates can suddenly find themselves with weird or subtle issues.
the simplest answer to "
Why can't I directly modify a component's state:
is all about Updating phase.
when we update the state of a component all it's children are going to be rendered as well. or our entire component tree rendered.
but when i say our entire component tree is rendered that doesn’t mean that the entire DOM is updated.
when a component is rendered we basically get a react element, so that is updating our virtual dom.
React will then look at the virtual DOM, it also has a copy of the old virtual DOM, that is why we shouldn’t update the state directly, so we can have two different object references in memory, we have the old virtual DOM as well as the new virtual DOM.
then react will figure out what is changed and based on that it will update the real DOM accordingly .
hope it helps.
It surprises me that non of the current answers talk about pure/memo components (React.PureComponent or React.memo). These components only re-render when a change in one of the props is detected.
Say you mutate state directly and pass, not the value, but the over coupling object to the component below. This object still has the same reference as the previous object, meaning that pure/memo components won't re-render, even though you mutated one of the properties.
Since you don't always know what type of component you are working with when importing them from libraries, this is yet another reason to stick to the non-mutating rule.
Here is an example of this behaviour in action (using R.evolve to simplify creating a copy and updating nested content):
class App extends React.Component {
state = { some: { rather: { deeply: { nested: { stuff: 1 } } } } };
mutatingIncrement = () => {
this.state.some.rather.deeply.nested.stuff++;
this.setState({});
}
nonMutatingIncrement = () => {
this.setState(R.evolve(
{ some: { rather: { deeply: { nested: { stuff: n => n + 1 } } } } }
));
}
render() {
return (
<div>
Normal Component: <CounterDisplay {...this.state} />
<br />
Pure Component: <PureCounterDisplay {...this.state} />
<br />
<button onClick={this.mutatingIncrement}>mutating increment</button>
<button onClick={this.nonMutatingIncrement}>non-mutating increment</button>
</div>
);
}
}
const CounterDisplay = (props) => (
<React.Fragment>
Counter value: {props.some.rather.deeply.nested.stuff}
</React.Fragment>
);
const PureCounterDisplay = React.memo(CounterDisplay);
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/ramda#0/dist/ramda.min.js"></script>
<div id="root"></div>
To avoid every time to create a copy of this.state.element you can use update with $set or $push or many others from immutability-helper
e.g.:
import update from 'immutability-helper';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
setState trigger re rendering of the components.when we want to update state again and again we must need to setState otherwise it doesn't work correctly.
My current understanding is based on this and this answers:
IF you do not use shouldComponentUpdate or any other lifecycle methods (like componentWillReceiveProps, componentWillUpdate, and componentDidUpdate) where you compare the old and new props/state
THEN
It is fine to mutate state and then call setState(), otherwise it is not fine.
My understanding was that PureComponent leverages shouldComponentUpdate() and does a shallow comparison of state and props but I created a little example that operates differently than I expected.
The example app displays a list of people. Each list item has the ability to change its name. Try it out and inspect the code here.
In the parent component, I have a method that looks like this:
updateName(id, newName) {
const { people } = this.state
const idx = people.findIndex(person => person.id === id)
people[idx].name = newName
this.setState({ people })
}
The object that I'm passing back into setState has the same reference as the previous object. In this case, shouldn't this component not update if it's doing a shallow comparison?
Secondly, another part that's unclear, I changed the child component Person from PureComponent to Component and I'm still getting the benefit of only the child that was updated re-rendering (I'm doing a console log on each child render if you want to inspect that). Clearly this is something that React is doing internally to decide for the child whether it should update but I assumed that if the component was re-rendering it would re-render everything.
shouldn't this component not update if it's doing a shallow comparison?
Yep. And it isn't updating. You can see that the App component's render() method does not get called again after the initial render. This means the shallow comparison is working as you expect and the component is (correctly) not updating.
I believe the part that's throwing you off is this line in Person.prototype.handleSubmit():
this.setState({ value: '' })
Since oldState.value !== newState.value, this will trigger a re-render on the modified Person component, regardless of whether Person is a PureComponent or not.
If you take this line out, you will see that nothing updates (the behavior you're expecting).
Clearly this is something that React is doing internally to decide for the child whether it should update.
Nope, the child is setting its own state. The parent-child relationship is irrelevant here. React will update the child directly.
In the future you should try isolating your tests. The reason this confusion occurred is because you were relying on the render() method to determine whether a child received new props. But the render() method is called when a child receives new props AND when a child sets new state.
A better test for this situation would have been checking whether componentWillReceiveProps() is called, thus eliminating state from the picture:
componentWillReceiveProps(newProps) {
console.log('receiving props', newProps)
}
If you plug this into the Person component, you will see that it does not get called. Exactly as you're expecting.
In short, React.PureComponent works exactly as you were thinking.
We have a crazy DOM hierarchy, and we've been passing JSX in props rather than embedding children. We want the base class to manage which documents of children are shown, and which children are docked or affixed to the top of their associated document's window.
List (crazy physics writes inline styles to base class wrappers)
Custom Form (passes rows of JSX to Base class)
Base Class (connects to list)
Custom Form (passes rows of JSX to base class)
Base class (connects to list)
The problem is that we're passing deeply nested JSX, and state management / accessing refs in the form is a nightmare.
I don't want to re-declare every row each time, because those rows have additional state attached to them in the Base Class, and the Base Class needs to know which rows actually changed. This is pretty easy if I don't redeclare the rows.
I don't know how to actually deal with rows of JSX in Custom Form.
Refs can only be appended in a subroutine of render(). What if CustomForm wants to measure a JSX element or write inline CSS? How could that JSX element exist in CustomForm.state, but also have a ref? I could cloneElement and keep a virtual DOM (with refs) inside of CustomForm, or depend on the base class to feed the deeply-nested, mounted ref back.
I believe it's bad practice to write component state from existing state. If CustomForm state changes, and I want to change which rows are passed to BaseClass, I have to throttle with shouldComponentUpdate, re-declare that stage document (maintaining row object references), then call setState on the overarching collection. this.state.stages.content[3].jsx is the only thing that changed, but I have to iterate through every row in every stage document in BaseClass when it sees that props.stages changed.
Is there some trick to dealing with collections of JSX? Am I doing something wrong? This all seems overly-complicated, and I would rather not worsen the problem by following some anti-pattern.
Custom Form:
render () {
return <BaseClass stages={this.stages()}/>
}
stages () {
if (!this._stages) this._stages = { title: this.title(), content: this.content() };
return this._stages;
}
title () {
return [{
canBeDocked: false,
jsx: (
<div>A title document row</div>
)
}
}
content () {
return [{
canBeDocked: false,
jsx: (
<div>Hello World</div>
)
}, {
canBeDocked: true,
jsx: (
<div>Yay</div>
)
}
}
What I usually do is just connect the lower level components via Redux. This helps with not passing the state in huge chunks from the top-most component.
A great video course by one of the React creators, Dan Abramov: Getting started with Redux
Absolutely agree with #t1gor. The answer for us was to use REDUX. It changed the entire game for us. Suddenly a button that is nested 10 levels deep (that is, inside a main view, header, header-container, left side grid, etc, etc, deeper and deeper) into purely custom components, has a chance to grab state whenever it needs.
Instead of...
Parent (pass down state) - owns state vars
Child (will pass down again) - parent has state vars
Grandchild (will pass down a third time) - grandparent has state vars
Great Grandchild (needs that state var) - great grandparent has state vars
You can do...
Parent (no passing) - reads global state vars
Child
Grandchild
Great Grandchild - also reads same global level state vars without being passed...
Usually the code looks something like this...
'use strict'
//Importation of Connection Tools & View
import { connect } from 'react-redux';
import AppView from './AppView';
//Mapping -----------------------------------
const mapStateToProps = state => {
return {
someStateVar: state.something.capturedInState,
};
}
const mapDispatchToProps = dispatch => {
return {
customFunctionsYouCreate: () => {
//do something!
//In your view component, access this by calling this.props.customFunctionsYouCreate
},
};
}
//Send Mappings to View...
export default connect(mapStateToProps, mapDispatchToProps)(AppView);
Long story short, you can keep all global app state level items in something called a store and whenever even the tiniest component needs something from app state, it can get it as the view is being built instead of passing.
The issue is having content as follows, and for some reason not being able to effectively persist the child instances that haven't changed (without re-writing the entire templateForChild).
constructor (props) {
super(props);
// --- can't include refs --->
// --- not subroutine of render --->
this.state = {
templateForChild: [
<SomeComponentInstance className='hello' />,
<AnotherComponentInstance className='world' />,
],
};
}
componentDidMount () {
this.setState({
templateForChild: [ <div className='sometimes' /> ],
}); // no refs for additional managing in this class
}
render () {
return ( <OtherManagerComponent content={this.state.templateForChild} /> );
}
I believe the answer could be to include a ref callback function, rather than a string, as mentioned by Dan Abramov, though I'm not yet sure if React does still throw a warning. This would ensure that both CustomForm and BaseClass are assigned the same ref instance (when props.ref callback is executed)
The answer is to probably use a key or createFragment. An unrelated article that addresses a re-mounting problem. Not sure if the fragment still includes the same instances, but the article does read that way. This is likely a purpose of key, as opposed to ref, which is for finding a DOM node (albeit findDOMNode(ref) if !(ref instanceof HTMLElement).
I am very new to React. On going through the React tutorial, I understand the need to lift the state to the parent component which ensures that the children components can stay in sync and can feed off the major chunk of data residing in the state of the parent. But then , with changing data, won't the setState in the parent fire off the re-rendering of all the children components, decreasing UI performance ? Without using flux or redux, which is the best way to position the state in a react application ?
When you change a component's state, it triggers a re-render for that component.
The re-render will create a new set of virtual elements (returned from the render function), which React uses to represent the new state of the DOM. However, unless there are differences between the virtual DOM and the real DOM, nothing will be changed.
The fact that React's virtual elements are simply lightweight object representations of actual HTML elements makes it fast enough that in most cases, you don't even need to think about the performance cost of calling render for child components.
If, however, you do find yourself with a child component that has a particularly expensive render function—you can prevent it from always re-rendering by implementing shouldComponentUpdate. This allows you to specify with fine grained control, which changes in props or state will actually trigger a component to update.
The other approach is to build a custom state management solution which explicitly ties your components together.
let state = { count: 0 };
let listenForState = null;
function ComponentA() {
let onClick = () => {
state.count += 1;
if (listenForState) listenForState(state);
};
return <button onClick={onClick}>+</button>;
}
class ComponentB extends React.Component {
constructor() {
super();
this.state = state;
}
componentWillMount() {
listenForState(state => this.setState(state));
}
render() {
return <span>{this.state.count}</span>;
}
}
However this quite quickly gets out of hand, and there aren't many situations where it would be better than just lifting state up to a shared parent.
If you find that your state driven child components are nested too deeply in the stateful parent, then it's time to look at Redux instead.
Yes, your mostly right. But you dont need to pass the state of the component to the children, you r passing only props, its not actually the same, because changing of props doesnt need to rerender your component.
There is amazing method in the component lifecycle, called shouldComponentUpdate, default its return true if only shallow equality of states objects are false (if refs are different - thats why mutate of state will not rerender component). And you can also implement this method by yourself, comparing your props to check if you need an update or not. There is a scheme how would it work.
And also, react will not rerender element if the virtual DOM was not changed. So this way you can rerender component if only his props is changed, but if you need to change props of its children - you must rerender current.
Basicly, because of these there is an advice to pull the state of application from top to the leafs component (which returns most of html). You can read about optimization more here.
And must to say :
Your code must become a spaghetti
If you will have a lot of components, which have a lot of events, based on state, so if you need some complex architecture, use Redux or some other global state manager.
If you are looking only for a State Management library, check Duix (https://www.npmjs.com/package/duix).
Redux solves a lot of problems (adding complexity). If you only want to improve the State Management, just take a look at Duix.
PS.: I created that library
After completing the guide on the Alt site I am confused if Alt uses Reacts state to set properties? In part 3 - using Stores, it says
Instance variables defined anywhere in the store will become the
state.
It then puts a variable in the store:
this.locations = [];
Yet if I go into the main React component and log the state, after locations is given data of course, I get undefined except on the props?
loggState() {
console.log(this.state); // undefined
console.log(this.locations); // undefined
console.log(this.props.locations); // [object][object]..
}
Can anyone explain the relationship between states and props when using Alt?
In Alt—and indeed most Flux implementations—the store is a totally different part of your application to your components.
A component subscribes to changes in a store, then uses the changed data to update its local state, causing it to re-render.
We derive the initial state of components that use the LocationStore from whatever the store's state is when we instantiate the component.
getInitialState() {
return LocationStore.getState();
},
Then we set up a subscription, listening for changes to the store.
componentDidMount() {
LocationStore.listen(this.onChange);
},
And finally, we use the subscription handler to apply these changes to the component's local state. The way you decide to apply each update is totally up to you.
onChange(state) {
this.setState(state);
},
Each time we call this.setState with a new state, the component will re-render.
You could also use a higher-order component to subscribe to the store, then covert the store's state to props and pass them down to a wrapped component, instead. In which case, your component wouldn't need to be stateful at all.