I’m trying to set some inline styles to children in React. I have this that actually works, sort of:
var App = React.createClass({
render: function() {
var children = React.Children.map(this.props.children, function(child, i) {
child.props.style.width = '100px'
})
return (
<div>{children}</div>
)
}
})
BUT, if I try to change styles when state changes, the styles will remain the same. You can easily see this when trying something like this: (fiddle: /)
var World = React.createClass({
getInitialState: function() {
return { width: 0 }
},
ponentDidMount: function() {
this.setState({ width: 100 })
},
ponentDidUpdate: function() {
console.log(this.getDOMNode().innerHTML);
},
render: function() {
var width
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width
console.log('Setting width: '+width);
child.props.style = {width: (i*this.state.width)+'px'}
return child
}, this)
return <div>{children}</div>
}
})
var Hello = React.createClass({
render: function() {
return (
<World>
<div>1</div>
<div>1</div>
</World>
)
}
})
React.renderComponent(<Hello />, document.body);
It will log that width has changed, but the props.style
is not working as expected. And using child.setProps()
will throw "Invariant Violation: replaceProps(...): Can only update a mounted ponent.". Is there another "react-way" of changing inline styles to children?
I’m trying to set some inline styles to children in React. I have this that actually works, sort of:
var App = React.createClass({
render: function() {
var children = React.Children.map(this.props.children, function(child, i) {
child.props.style.width = '100px'
})
return (
<div>{children}</div>
)
}
})
BUT, if I try to change styles when state changes, the styles will remain the same. You can easily see this when trying something like this: (fiddle: http://jsfiddle/9UvWL/)
var World = React.createClass({
getInitialState: function() {
return { width: 0 }
},
ponentDidMount: function() {
this.setState({ width: 100 })
},
ponentDidUpdate: function() {
console.log(this.getDOMNode().innerHTML);
},
render: function() {
var width
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width
console.log('Setting width: '+width);
child.props.style = {width: (i*this.state.width)+'px'}
return child
}, this)
return <div>{children}</div>
}
})
var Hello = React.createClass({
render: function() {
return (
<World>
<div>1</div>
<div>1</div>
</World>
)
}
})
React.renderComponent(<Hello />, document.body);
It will log that width has changed, but the props.style
is not working as expected. And using child.setProps()
will throw "Invariant Violation: replaceProps(...): Can only update a mounted ponent.". Is there another "react-way" of changing inline styles to children?
2 Answers
Reset to default 6You are modifying the props object of each child directly, which bypasses parts of React's update mechanisms. We will be making this impossible soon.
In this case you want to use cloneWithProps
inside your World
ponent's render function.
var children = React.Children.map(this.props.children, function(child, i) {
width = i*this.state.width;
console.log('Setting width: '+width);
return React.addons.cloneWithProps(child, {style: {width: width + 'px'}})
}, this)
return <div>{children}</div>
cloneWithProps
creates a new copy of the ponent and merges in additional props. You should use this pattern to ensure you're giving React all the hints it needs to know when to update.
Here's your example working: http://jsfiddle/zpao/Tc5Qd/
Just after the "Can only update a mounted ponent" check, there is another more descriptive error message:
invariant(
this._mountDepth === 0,
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
'ponent with a parent. This is an anti-pattern since props will ' +
'get reactively updated when rendered. Instead, change the owner\'s ' +
'`render` method to pass the correct value as props to the ponent ' +
'where it is created.'
);
The logic es from a different perspective than updating DOM nodes. When a DOM node needs to be changed, the way to do that normally is to get a reference to the DOM node, then set its properties. With React, instead of updating particular nodes, the render methods just return new React ponents with the correct properties set, then the back-end code works out which DOM nodes to update.
When something is shown wrongly in normal DOM code, it is hard to know where the problem is, since any code can update the DOM nodes.
When the code is arranged into render methods like in React, the render methods always take in this.props
and this.state
and return entire, fully rendered, ponents. Then, if something isn't rendered properly, it is always possible to look in the render
method to find the problem, since that is the only place where that render happens. (And this works whether it is the first render or a second render, so there doesn't need to be a distinction between rendering ponents the first time, and updating ponents.)
So, the way around the problem you're describing, is to move the logic about rendering the divs into the render method which creates the divs, rather than in the render method of a different ponent. Something like this:
var World = React.createClass({
render: function() {
return <div>{this.props.children}</div>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return { width: 0 };
},
ponentDidMount: function() {
this.setState({ width: 100 });
},
render: function() {
var children = ["a", "b", "c"].map(function(content, i) {
return (
<div style={{ width: i * this.state.width }}>
{content}
</div>
);
}, this);
return (
<World>
{children}
</World>
)
}
});
React.renderComponent(<Hello />, document.body);
http://jsfiddle/kN4DV/2/