最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Set inline style of children - Stack Overflow

programmeradmin1浏览0评论

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?

Share Improve this question asked Jun 29, 2014 at 9:19 David HellsingDavid Hellsing 109k44 gold badges180 silver badges214 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

You 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/

发布评论

评论列表(0)

  1. 暂无评论