I've got a React component hierarchy that looks a little like this:
var A = React.createClass({
render: function() {
/* stuff */
}
});
var B = React.createClass({
render: function() {
return dom.div(null, this.props.children);
}
});
var C = React.createClass({
render: function() {
return dom.div(null, B(null, A()));
}
});
Essentially, C passes an A element to B as a child, and B subsequently renders it.
Now I'm in a position where I want to update the A element but not B. It's unclear to me what the semantics of shouldComponentUpdate are in this case. If I implement a shouldComponentUpdate in B, do I need to consider whether or not my children should update? Is it impossible to update the child without updating it's immediate parent?
If you want to update A but not B, you should actually describe A behaviour in it's own component - for example, subscribe to some third-party source of truth (Store in terms of Flux architecture) in A's componentDidMount hook.
This is the only way - if A has no control on it's own flow, it will update only on B (exectly parent) render() call - which you can already control by B's (and \ or it's parents') shouldComponentUpdate.
Related
Let's say I have a react component - a button - that increments a value when I click it.
For example
var Component = React.createClass({
getInitialState: function() {
return {count: 0}
},
increment: function() {
this.setState({count: this.state.count + 1})
},
render: function() {
return (<button onClick={this.increment}>{this.state.count}</button>);
}
})
React.render(<Component />, document.getElementById('react-container'));
The state is mutable!
I can do a similar thing with props
var Component = React.createClass({
increment: function() {
this.setProps({count: this.props.count + 1})
},
render: function() {
return (<button onClick={this.increment}>{this.props.count}</button>);
}
})
React.render(<Component count={0}/>, document.getElementById('react-container'));
The state is mutable!
Some of the resources I have checkouted out say props are immutable, but then go and do something like setProps. The different resources keep contradicting each other. This is making it difficult for me to grasp the difference between state and props.
Are props mutable? If not why can I change them? It seems like changing props is not good practice, is this true? If yes, why does setProps exist?
this.props and this.state can be be mutated but this is really just a consequence of the mutability of objects in JS.
Your assertion that this.setState mutates the current state is false; a new object representing the state is created, with the changes applied.
To be honest I didn't even know that setProps existed but it sounds like a complete anti-pattern! The whole point of props is that they are passed through to the component, enforcing the unidirectional data flow. If the component can change its own props, this flow is broken.
You should really aim to store as much of your application state as possible at the top level i.e. in a store, rather than using component state. If all the state is in one place, it becomes a lot easier to reason about it.
setProps is a holdover from the early days of React, and it's been deprecated for a long time now. You're correct, changing props from within the component isn't a good practice - think of them as like the arguments to a function. Rather than mutating props, you should either:
Use a callback prop to notify the parent that something has happened - the parent can then mutate the data it owns and pass it back into the child through props.
Make use of component state as you did in your first example.
What both of these solutions have in common is that a single component owns the data, and that's the only component that's allowed to modify it - this is commonly referred to as there being a 'Single Source of Truth'. The great thing about structuring your code this way is that it means that you don't get stuck in tangles of spaghetti code trying to work who's mutating a piece of data.
What are the best practices for filtering a Immutable.List for passing down to child components as regards to shouldComponentUpdate?
Illustrative example: I'm building a calendar. It has a <Week> component, holding seven <Day> components. The <Week> receives a list of all calendar events, then renders the <Day>s and passes a filtered subset of events to each of them as props.
All the <Day> components have PureRenderMixin attached to them, with the intent of preventing useless re-renders. But, given that Immutable.List.filter returns a new object each time, when a single event is added, every <Day> component will receive a 'new' event list as prop and re-render, even when all but one of them have the same content.
My current idea is writing a custom shouldComponentUpdate that compares hashCodes of every list component decides to re-render based on that. Is there a better, more established way to do it?
Code example as requested. Assume <Week> is wired up to a flux store and gets the events from it.
Day = React.createClass({
propTypes: { events: React.PropTypes.instanceOf(Immutable.List) },
mixins: [ React.addons.PureRenderMixin ],
render: function(){
const events = this.props.events.map((event) => {
return <div key={event.get('id')}>{event.get('name')}</div>
})
return <div>{events}</div>
}
})
Week = React.createClass({
propTypes: { events: React.PropTypes.instanceOf(Immutable.List) },
mixins: [ React.addons.PureRenderMixin ],
render: function(){
const days = [1,2,3,4,5,6,0].map((weekday) => {
const dayEvents = this.props.events.filter(event => event.get('weekday') === weekday)
return <Day events={dayEvents} key={weekday} />
})
return <div>{days}</div>
}
})
You should restructure the model like this-
{'Monday':{...events...}, 'Tuesday':{...events...}, ...}
This way you can pass the right set of events to the right child Day component.
Update
Noticed you are using numbers for weekdays, simply substitute day names with numbers, or use a List/array to store events. Basically we are doing away with the need of calling filter.
I have come across a problem about states based on properties.
The scenario
I have a Component parent which creates passes a property to a child component.
The Child component reacts according to the property received.
In React the "only" proper way to change the state of a component is using the functions componentWillMount or componentDidMount and componentWillReceiveProps as far as I've seen (among others, but let's focus on these ones, because getInitialState is just executed once).
My problem/Question
If I receive a new property from the parent and I want to change the state, only the function componentWillReceiveProps will be executed and will allowed me to execute setState. Render does not allow to setStatus.
What if I want to set the state on the beginning and the time it receives a new property?
So I have to set it on getInitialState or componentWillMount/componentDidMount. Then you have to change the state depending on the properties using componentWillReceiveProps.
This is a problem when your state highly depends from your properties, which is almost always. Which can become silly because you have to repeat the states you want to update according to the new property.
My solution
I have created a new method that it's called on componentWillMount and on componentWillReceiveProps. I have not found any method been called after a property has been updated before render and also the first time the Component is mounted. Then there would not be a need to do this silly workaround.
Anyway, here the question: is not there any better option to update the state when a new property is received or changed?
/*...*/
/**
* To be called before mounted and before updating props
* #param props
*/
prepareComponentState: function (props) {
var usedProps = props || this.props;
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === usedProps.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
componentWillMount: function () {
this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
/*...*/
I feel a bit stupid, I guess I'm loosing something...
I guess there is another solution to solve this.
And yeah, I already know about this:
https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
I've found that this pattern is usually not very necessary. In the general case (not always), I've found that setting state based on changed properties is a bit of an anti-pattern; instead, simply derive the necessary local state at render time.
render: function() {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === this.props.currentQuestion.id;
});
return ...; // use currentResponses instead of this.state.currentResponses
}
However, in some cases, it can make sense to cache this data (e.g. maybe calculating it is prohibitively expensive), or you just need to know when the props are set/changed for some other reason. In that case, I would use basically the pattern you've written in your question.
If you really don't like typing it out, you could formalize this new method as a mixin. For example:
var PropsSetOrChangeMixin = {
componentWillMount: function() {
this.onPropsSetOrChange(this.props);
},
componentWillReceiveProps: function(nextProps) {
this.onPropsSetOrChange(nextProps);
}
};
React.createClass({
mixins: [PropsSetOrChangeMixin],
onPropsSetOrChange: function(props) {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
// ...
});
Of course, if you're using class-based React components, you'd need to find some alternative solution (e.g. inheritance, or custom JS mixins) since they don't get React-style mixins right now.
(For what it's worth, I think the code is much clearer using the explicit methods; I'd probably write it like this:)
componentWillMount: function () {
this.prepareComponentState(this.props);
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
prepareComponentState: function (props) {
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
I am new to ReactJS and had been through some examples: flux-todo-mvc.
Since react keeps component in a tree structure and updates on state change.
How can I add a new child component (not bundled) at specific node of tree without page refresh?
More Description
Suppose component X can have three types of child: A, B and C. At first GET request it is always A so X -> A is rendered and to make it fast we did not include B and C. For example:
X = React.createClass({
render: function() {
return (
<A />
);
}
});
React.render( <X />, document.getElementById('_x') );
After this, with some change in state can we change child of X to B or C using AJAX request
You can write conditional logic inside your render function to set a variable to null or a component instance. And inject that inside the returned render JSX.
Such as:
render: function() {
var subComponent = null;
if (this.state.showSub) {
subComponent = <Child />;
}
return (
<div>{subComponent}</div>
);
}
Only way to do it with state changes in real time. Can't update the render code in real time without an actual refresh.
you can do something like this:
render: function() {
var child = function(){
if(this.state.ajax_result ==1) {return <B/>
else if(this.state.ajax_result ==2) {return <C/>}
else {return <A/>}
}
return ( {child} );
}
now outside of render, listen to your ajax calls. every time the state will change, your component will re-render
I'm very new to reactjs (three days, in fact). I want to construct a component that lays out its children according to a policy (implemented by the component) and certain children properties. Some properties are passed directly to the children as props upon creation; some other properties, such as its dimensions, are intrinsic to each child. That is, this Layout component will be used like:
<Layout>
<Child1 propx=... propy=...>...</Child1>
<Child2 propz=...>...</Child2>
...
</Layout>
and the position of the children will depend on propx, propy, propz, ..., and the children sizes.
Well, the Layout component needs to know the values of its children props and their sizes. At first, I gave the children classes a method 'getProperties' to return the relevant properties and I invoked the method in React.Children.forEach(this.props.children, fn) but I found that the object passed to fn is not the component instance so I cannot call getProperties on it. Then I tried to pass each Layout child an extra property 'uploadProperties' with a callback to be called by the child to tell the Layout what it needs to know. I know I cannot call setProps in the children, so I came up with this:
var Layout = React.createClass({
storeChildProperties: function (properties) {
this.childData = this.childData || [];
this.childData.push(properties);
},
render: function () {
var self = this;
var children = [];
React.Children.forEach(this.props.children, function (child) {
var clone = React.addons.cloneWithProps(
child,
{key: child.props.key, uploadProperties: self.storeChildProperties});
children.push(clone);
});
this.props.children = children;
return <div>{this.props.children}</div>;
}
}
It seems to work, but I feel uneasy about two things. First, I don't like attaching 'childData' to the Layout instance (I didn't know where else to put it). Second, I don't know if it's ok to replace this.props.children. Actually, it works even if I don't and I just
return <div>{children}</div>;
Anyway, is this a reasonable way to get info from children?
You can access children's props via this.props.children[0].props.