I’m working on a React Flux project where async interaction is an ongoing thing and I’m still a bit wonky when it comes to the react lifecycle.
In my statefull components I have a getStateFromStores function, if there is no state in store, this returns a false bool, otherwise it returns the state.
As I have it right now the cycle is as following:
I have one bound function (ES6 react class) in my components called getState(), this simply invokes getStateFromStores and checks that the return is not false, and if not, invokes setState() with the state.
Since notifications of state change can come from different sources, I like to declare the logic in one place (getState)
State change can come from the following:
ComponentDidMount (should I move this to initialState?)
This is the first thing that happens in the components lifecycle. (And unless state already exists in our store, we get false in return)
ComponentWillReceiveProps
If state is changed in parent component, we notify the component that it should fetch new state with the new props.
Store change listener
When store has new state, getState is invoked as the callback (although a bit problematic, because emit can happened for reasons other than as a consequence of the given component’s call – so I might implement an onStoreChange as the callback who in turn calls getState)
Im toying around with a state prop called fetching which I can use to control the flow, but is this problematic? – because every time I change state it triggers a render (?) — could I use static for this?
I imagine something of the following; DidMount triggers getState(), getState sets fetching to true, and now all other invoke-tries will fail because of the bool check.
Then I’d implement a resetState, that brings back the state to default (fetching false) and then getState can be triggered again.
I’d abstract all this functionality in a “Higher-Order component”
Thanks!
I take a different approach. I've found that one of the great advantages to writing React components is that you're able to think of the component as a self-contained unit -- with its own HTML, JavaScript, data-fetching -- even styles. It relies on Flux stores to keep track of data, as opposed to passing that data as props.
Suppose you have a user log in and receive back some information. Now, let's say the user wants to manage their account. We could do something like this...
<Account user_info={this.state.user_info} />
That will work fine, but can't the Account get the user_info it needs if it knows the user_id? That would lead us to...
<Account user_id={this.state.user_info.user_id}>
But, really, doesn't the store know who the logged-in user is? After all, it was integral in getting/storing that data. So why not just...
<Account />
...and let Account get the data it needs from the store -- no user_id needed, since the store already knows the logged-in user.
Anyway, just a different way to think about things.
What I've found working really well for me in most cases is to have a root "controller component". Let's call it Controller for now. It's basically a React component just as any other – but its responsibility differs a bit.
I see it as solely delegating. It's only responsibility is to get data (state) from the stores and pass it down (as props) to its child components and let them do all the work.
This is done by setting the data from the stores as the Controller's state. Any changes in the stores are reported back to (only) the Controller, which updates its state. Since the state is passed down as props to the children, a change in a store will trigger all child components using that data to re-render (if they have to).
I try to pass down only relevant data to the child components, but don't worry to much about them getting to much data (or about some extra re-rendering).
In practice the Controller would look something like this:
function getStateFromStores() {
return {
players: PlayerStore.getData(),
cards: CardStore.getData()
};
}
var Controller = React.createClass({
getInitialState: function() {
return getStateFromStores();
},
componentDidMount: function() {
PlayerStore.addChangeListener(this._onChange);
CardStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
PlayerStore.removeChangeListener(this._onChange);
CardStore.removeChangeListener(this._onChange);
},
_onChange: function() {
this.setState(getStateFromStores());
},
render: function() {
return (
<Component1
players={this.state.players}
cards={this.state.cards}
/>
<Component2
players={this.state.players}
/>
<Component3
cards={this.state.players}
/>
);
}
});
module.exports = Controller;
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.
What I would like to do:
I have various self-contained components, that manage their own state within themselves. The state of each component should definitely be handled within each component.
Their initial state comes from the back-end. After this they maintain their own state.
I don't want to make lots of separate requests for each component, so I make one request to fetch the data for all of the components, and pass this down which provides initial data for each component.
My current Solution:
const MyApp = () => {
const [initialState, setInitialState] = useState();
useEffect(()=>{getInitialState().then((data)=>{
setInitialState(data);
})},[])
if (!initialState) {
return <div>Loading...</div>
}
return (
<>
<ComponentA initialData={initialState.A} />
<ComponentB initialData={initialState.B} />
<ComponentC initialData={initialState.C} />
</>
)
}
Problems with my current solution:
The above works, but it doesn't feel right. I've called the above initialState but it shouldn't be state. It's much more like a prop, and it's confusing to store the initial data for the entire life-time of the app. Admittedly, it's not doing anything, and doesn't cause any harm, but if it's not being used by anything, why keep it there?
I considered fetching the data before I even rendered the app, and then passing the data in as a prop, but then I would have nothing displayed rather than a loading screen, so that didn't seem like a good approach either.
My Question:
Is there a better (and/or standard) approaching for achieving the above? (I'd like to use hooks rather than classes). Perhaps the above isn't such a bad idea, but it feels like there must be probably a better way of doing it without resorting to 'global' app state. I would like to keep my state close to the components where the relevant parts of it are used.
There is nothing wrong with using state here. However, assuming that getInitialState is only supposed to be called once, I would wrap it in the useEffect hook, such that the data is only fetched once when MyApp is mounted. This will prevent getInitialState from being called repeatedly when the component re-renders.
useEffect(() => {
getInitialState().then((data)=>{
setInitialState(data);
});
}, []);
If you do not wish to use state to maintain the data, you will have to rely on the Context API, or use some kinda state management library such as Redux.
Basically, I have a component called Timeperiod which is a dropdown where users will select values such as "today", "yesterday", "last week", etc.
I want to re-use the <Timeperiod> component in a lot of other components and the problem is I wouldn't be able to access which value they chose if I call it in another.
I know an alternative would be to call my other components inside of Timeperiod and passing them properties based on Timeperiod state but wouldn't that mean I need to create multiple Timeperiod like classes?
Would redux or something be a good way to handle these situations?
Here's my codesandbox: https://codesandbox.io/s/ypyoq8zoz
Right now I only have App.js calling Timeperiod, but I will have many more classes calling it too. I want to be able to access the date chosen in App.js while making the Timeperiod class re-usable.
To decide if you need Redux and how much of Redux do you need, you have to consider carefully the whole application you are developing. Redux adds some boilerplate, which might be too much for the benefits you would gain.
You can however solve your problem without Redux.
Without Redux
One of the key ideas under React is called "Lifting state up". This means that if several components need access to the same piece of data, you should definitely move the storage of that piece of data in a component that is a common ancestor of all those components (see https://reactjs.org/docs/lifting-state-up.html).
In your case, it should not be responsibility of Timeperiod to keep memory of the selected date, but of App (indeed, the closest common ancestor of all components that need access to the selected period). Timeperiod should take the selected date from app via a property, and notify App when user changes the selected date using an event (again, a prop containing a function).
class App {
constructor(props) {
super(props);
this.state = {
start: new Date(), // initial value
end: new Date(), // initial value
}
}
render() {
return (
<Timeperiod
start={this.state.start}
end={this.state.end}
onStartChange={newValue => this.setState({ start: newValue })}
onEndChange={newValue => this.setState({ end: newValue })}
/>
);
}
}
With Redux
Redux allows you to keep a global state and access it in special components called Containers. You can put how many containers you want, in any point of the document tree. This seems great, but has several drawbacks:
Too many container components degrade performance
Having full access to whole state in any point of the tree could create problems if you are not super careful. A component could modify some data it should not be allowed to access, and so on.
Redux introduces some boilerplate that might be too much if the application is simple
For any update you have to define some actions capable of handling it, create a reducer to produce the next state from the previous state and the action, and connect together all these pieces
Conclusion
It really depends on your application whether Redux is good or bad, but if your component hierarchy is not too deep and your app not too complex, vanilla way could be better
So, to follow up our comments. Here's how you could approach it:
The Timeperiod component expects a getDate function that expects a title argument. When you render the Timerperiod component, each time it has a separate state and props.
Check out how I rendered more than one(in the app.js), to show that in action.
Using redux what you could do is have a within your state a timePeriod sub state handled by a dedicated reducer which stores the user's choice.
Then each of your TimePeriod component will be hooked to this state using something like
const ConnectedTimePeriod = connect(
state => state.timePeriod,
{
onTimePeriodChoose (timePeriod) {
return {
type: "CHOOSE_TIME_PERIOD",
timePeriod
}
}
}
)(TimePeriod);
Which hydrates your component with the global shared state and provides a function as a prop to update the time period.
The ConnectedTimePeriod can then be used everywhere and will share the global state.
I would suggest you take a look at the react-redux docs for example and details.
I don't feel right to store a function in a reactjs state, but not sure any disadvantage? Could anyone help analyze it?
view1: function() {
return <div>view1</view>
}
view2: fucntion() {
return <div>view2</view>
}
goView1: function() {
this.setState({contentView: view1})
}
goView2: function() {
this.setState({contentView: view2})
}
render: function () {
return(
{this.state.contentView()}
)
}
There're a few common gotchas about state in react:
Don't mutate state directly, use setState instead
Don't use state when not referenced in render, use instance variable instead.
Well, it seems fine to use state as function, since it's used in your render function to return what to render? Alright, it might not be an anti-pattern, but a better place to put those functions might be in your props, aka render prop, a few reasons:
Think state as your component's data model.
Data are not stale, it changes over time, it could be results from async operations, ui states after user interactions, controlled forms values etc.
Just like redux stores your app's global state, react state should store your component's local state that changes over time, while functions most of time, does nothing more than deferring execution.
Render props is different, it offers IoC (Inversion of Control) for free.
Props most of time similar to local state, should be concerned with UI rendering. However, unlike state owned/managed by component itself, props are passed from parents/clients of the component.
Event handlers for example, event target (usually DOM elements like button, input etc) doesn't need to know how to handle events when creating the component, instead it yields control back to component client/consumer who might need to use setState in its handleEventName handler to update certain container's state shared across children.
Some library (e.g., react router4) also shifted from HOC to render props (e.g., react 16.3 new context API - provider/consumer pair) in favor of its flexibility and explicitness. Instead of composing HOCs before runtime for static version of final enhanced component, you can declare what to render using render props and composing them at runtime in render functions. Thus, you get a clear separation between data (state/props) provider and consumer (where render props are passed from).
Regarding any performance (re-render) concerns, i think it's totally fine to use state as function, since most performance tricks are done by using shouldComponentUpdate hook with shallow compare state/props references (e.g. React.PureComponent, Redux Connect etc), so it's where immutability and memoization comes into play.
If that the flux store is a singleton that maintains the state of the data why do the components use setState and not setProps when accessing the stores? Wouldn't it just mean that I started saving the application state in two (or more) places?
Both the Flux / React documentation and Examples seem to point to setState as the preferred solution, but I've had an interesting conversation with a few colleagues at work and wondered if anyone else came across this
Edit:
You can see what I'm talking about in this url:
https://github.com/facebook/flux/blob/master/examples/flux-chat/js/components/ThreadSection.react.js
Notice how ThreadSection is a child component, that is fetching data directly from a store and using it as a state.
If you follow the React "way" I would have expected the state to be managed by the store - not a child component.
The solution we thought of is to fetch all stores in the top level component (as props) and pass them down to the child components as needed. But that gets rather ugly rather quickly.
We do that because setProps does not work on child components
Understand that you should have 2 kinds of components. Stateful components and view components.
Stateful components can have 3 kinds of states: initial state, user input state, and data store state.
Stateful components are like small entry points in the "widget" that you're assembling. There is no single application-wide entry point anymore for downstream dependency or data injection, because all of these widgets have their own isolated lifecycles. That's why they themselves need to access & listen to stores.
Besides behavorial properties, stateful components do not receive actual data via upstream properties.
Stateful components manage their own state and pass it to their children to render through downstream properties.
Stateful components do not normally render html DOM elements themselves directly. They're more like the controllers in MVC, and use other dumber components, the ones like views in MVC, to actually render DOM elements.
Dumber components are like views so they only contain logic to render DOM elements. Think of them as handlebars.js templates that only receive properties, and simply render those into DOM elements possibly with loops etc. They are stateless renderers.
Hope this answers your question.
According to formal documentation, a store should update the parent component's state, and pass it down via his children props:
When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own setState() or forceUpdate() methods, causing its render() method and the render() method of all its descendants to run.
We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.
(facebook flux docs - Overview)
It makes more sense to put store data in the component's state, this is because props may change by a parent component with componentWillReceiveProps. So it makes sense to update the state whenever:
the store's change event is fired and
whenever the props change (putting derivative data related only to the component itself to the state)
Below is a sample component that updates listening to a reflux store and also on props change. I rarely use this.props in the render function, instead I amend them (create derivative data that is only used within the component itself) as new props come in. I constantly run into this pattern so might as well write this down:
var SampleComponent = React.createClass({
mixins: [Reflux.ListenerMixin],
// reusable helper function to build state object
buildStateFromProps: function(props) {
return {
actualHeight: props.height + 20
}
},
// default props if no such was set by a parent component
getDefaultProps: function() {
return {
height: 100
};
},
// initial state with all value set to something default
// even using buildStateFromProps with default props
getInitialState: function() {
// this.props is built before this.state
var state = buildStateFromProps(this.props);
// append default data from store
state.text = '';
},
// happens when the parent component send different
// props data
componentWillReceiveProps: function(nextProps) {
// building derivative data from new props
// reusing buildStateFromProps
this.setState(buildStateFromProps(nextProps));
},
// setting up store to be used by the component
componentDidMount: function() {
// this.listenTo is a helper function ListenerMixin
this.listenTo(sampleStore, sampleUpdated);
},
// is called from the sampleStore update
sampleUpdated: function(sampleData) {
this.setState({
text: sampleData.text
});
},
render: function() {
return (
// ...
// using this.state.text from store updates and
// this.state.height from prop updates
);
}
});
The reason I send props data to state is to avoid cluttering up the render function. Otherwise the render function will contain a lot of code that is not really related to "rendering" the component. Furthermore if this derivative data is used in other parts of the application then it is easy to pull it out from the component and put it into the store.
Hope this helps.
A valid answer to this question sits hidden in the comments to a previous answer:
#idolize you can also pass stores in using React contexts (a hidden,
not yet officially documented feature). It's really nice because you
don't have to do all that passing down the hierarchy. There are a few
articles about contexts, search for it online! – Andy Jul 17 '15 at
18:41