Javascript 更新props后,this.props始终===nextProps/prevProps

Javascript 更新props后,this.props始终===nextProps/prevProps,javascript,reactjs,Javascript,Reactjs,我这里有一个关于这个问题的jsbin: 在onClick事件中,我将从模型中删除一个元素,并重新呈现应用程序。但奇怪的是,在componentWillReceiveProps()中(componentWillUpdate,componentdiddupdate也是),nextrops总是===到this.props,不管我做什么 /** @jsx React.DOM */ var Box = React.createClass({ render: function() { retur

我这里有一个关于这个问题的jsbin:

在onClick事件中,我将从模型中删除一个元素,并重新呈现应用程序。但奇怪的是,在componentWillReceiveProps()中(componentWillUpdate,componentdiddupdate也是),nextrops总是===到this.props,不管我做什么

/** @jsx React.DOM */
var Box = React.createClass({
  render: function() {
    return (
      <div className="box" onClick={ UpdateModel }>
        { this.props.label }
      </div>
    );
  }
});

var Grid = React.createClass({
  componentWillReceiveProps: function(nextProps) {      
    // WTF is going on here???
    console.log(nextProps.boxes === this.props.boxes)        
  },
  render: function() {
    var boxes = _.map(this.props.boxes, function(d) {
      return (<Box label={ d.number } />);
    });

    return (
      <div className="grid">
        { boxes }
      </div>
    );
  }
});

var model = [
  { number: 1 },
  { number: 2 },
  { number: 3 },
  { number: 4 },
  { number: 5 }
];

function UpdateModel() {
  React.renderComponent(
    <Grid boxes={ _.pull(model, _.sample(model)) } />,
    document.body
  );
}

React.renderComponent(
  <Grid boxes={ model } />,
  document.body
);
/**@jsx React.DOM*/
var Box=React.createClass({
render:function(){
返回(
{this.props.label}
);
}
});
var Grid=React.createClass({
componentWillReceiveProps:function(nextProps){
//世跆联会在这干什么???
console.log(nextrops.box===this.props.box)
},
render:function(){
var-boxes=uz.map(this.props.boxes,函数(d){
返回();
});
返回(
{box}
);
}
});
var模型=[
{编号:1},
{编号:2},
{编号:3},
{编号:4},
{编号:5}
];
函数UpdateModel(){
React.renderComponent(
,
文件正文
);
}
React.renderComponent(
,
文件正文
);

在componentWillReceiveProps()生命周期事件中通过UpdateModel()更新后,我需要nextProps与this.props不同

==
将检查它是否是同一个对象。您所做的似乎是改变
属性所指向的值。

类似的情况发生在我使用Flux存储以官方Todo列表教程()建议的方式存储状态时。对于在完成本教程后发现此问题的任何其他人,getAll()方法中的TodoStore中似乎出现了一个问题,因为它返回对内部数据对象的直接引用:

getAll: function() {
  return _todos;
}
这似乎破坏了生命周期方法(如componentDidUpdate(prevProps))区分新旧道具的能力。我认为原因在于,通过将对存储的数据对象的直接引用传递到视图中,当存储发生更改时,状态/道具实际上会立即更改,而不是在通过生命周期方法传递新值之后,因此旧道具和新道具始终包含相同的值。这可以通过传递内部数据对象(而不是对象本身)的副本来解决

例如,当_todos是一个对象时

getAll: function() {
  return JSON.parse(JSON.stringify(_todos));
}
如果_todos是数组,则可以使用return _todos.slice()


通常,如果使用存储来包含状态信息并从控制器视图调用它,则最好返回数据的副本,而不是原始数据的引用,因为当状态发生更改时,原始数据将发生变化。

您可以使用Immutable.js。这是Facebook为与react/flux合作而制作的一个库。

我们已经被这件事难倒好几次了

shouldComponentUpdate(nextProps) { 
    return this.props.myObj !== nextProps.myObj
}
很多时候,myObj对象中的某些值会发生变化。但是,上述函数将返回false。原因是
this.props.myObj
nextrops.myObj
都引用/指向同一个对象

通过实现Immutable.js,数据将始终作为克隆传递(而不是引用同一个对象,它们实际上是单独的对象)

它加强了通量的单向流动。您将永远无法(意外地)修改传递到组件中的原始数据(无论是作为道具还是从flux存储)


这还允许您使用PureRenderMixin–如果状态/道具的值没有更改,它会自动停止渲染。这可能有助于提高性能。

实现Immutable.js的一个替代方法是使用对象扩展运算符

例如,

假设您有一个要修改的变量,然后对其进行处理

const obj1 = {a: 1, b: 2}
const obj2 = testObj
obj2.a: 3


// result: obj1 = obj2 = {a:3, b:2}
将修改原始对象。这是因为obj2变量实际上只是指向obj1对象的指针

现在假设我们使用扩展运算符

const obj1 = {a: 1, b: 2}
const obj2 = {...testObj}
obj2.a: 3

// result: obj1 = {a:1, b:2}, obj2 = {a:3, b:2}

这将导致obj2正确更新,但obj1保持不变。spread操作符可以有效地创建obj1的浅克隆。我发现扩展运算符在修改redux减缩器中的状态时特别有用。如果您直接将变量设置为state并进行修改,则会导致您遇到的问题。

如果您不想使用扩展,可以改为使用扩展。只需将
UpdateModel
函数更改为

UpdateModel(){
  React.renderComponent(
    <Grid boxes={ _.clone(_.pull(model, _.sample(model))) } />, document.body
  );
}
UpdateModel(){
React.renderComponent(
,document.body
);
}

否则,具有的nextrop.box对象将与this.props.box对象是同一个对象,因此对其所做的任何更改都将反映在这两个对象中

最后我把它修好了问题是更改道具时没有深度克隆道具。

我的道具是一个
对象数组
,我在父对象上用以下方法更改它:

let newtodos = [...this.state.todos];
newtodos[0].text = 'changed text';
this.setState({ todos : newtodos });
所以在
componentdiddupdate
中,我得到了错误的结果:

componentDidUpdate(prevProps){
  //prevProps.todos is always equal to this.props.todos, since you changed the prop object directly.
  if(JSON.stringify(prevProps.todos) !== JSON.stringify(this.props.todos){...}
}
所以我用以下方法解决了这个问题:

// let newtodos = [...this.state.todos]; // SHALLOW COPY - Wrong
let newtodos = JSON.parse(JSON.stringify(this.state.todos)); // Deep Clone - Right
newtodos[0].text = 'changed text';
this.setState({ todos : newtodos });

为什么不使用状态?因为它不适合我构建的用例。我正在尝试构建一些生命周期方法,这些方法允许我将动态道具传递给卸载组件,因此首先我需要确定哪些组件正在卸载,因此在componentWillReceiveProps()中进行比较。我遇到了同样的问题。当调用componentWillReceiveProps时,当我尝试访问this.props时,它已经与nextProps相同。我无法访问旧道具来比较它们。我的道具肯定变了,在组件收到道具之前,我无法访问道具。将此标记为答案,因为它有点接近。我通过添加model=model.slice()解决了这个问题;到UpdateModel()方法的第一行。如果您想在不调用setState的情况下改变模型,您可以始终对
React.renderComponent
:)返回的组件实例调用forceRender())我们需要创建一个