React Component的状态与.unshift()异常呈现,但与.push()一样正常

时间:2017-07-19 19:47:54

标签: javascript arrays reactjs

我有一个数组,我有2个组件(子组件和父组件)。我遍历父组件中的数组,我渲染子组件,我给它们带有数组数据的道具。

子组件将其道具转换为状态,然后递增和递减该状态。

父组件可以将新项添加到数组中并重新渲染。但。如果我在数组前面的unshift()新项目,我将数组中的最后一项添加到屏幕而不是前面的新项目。

问题:为什么用.push()渲染好,用.unshift()渲染不好。使用concat和[newItem,... oldArray]一切都还可以,但是当我在数组前面添加项目时,同样的东西很糟糕?另外如何正确地将.unshift()新项目(评论,计数器,图像,帖子,例如任何东西)放入状态,以便首先渲染?

PS:我做的任何事情(concat,slice,...数组,非移位,反应的不变性助手)似乎无法正常工作。 Mobx和Redux没有帮助。

PS:Mithril,Inferno和Aurelia也会出现这种情况。

import React from 'react'
import {render} from 'react-dom'
var Component = React.Component

var data = [0, 12, -10, 1, 0, 1]

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            counter: data
        }
        this.addCounter = this.addCounter.bind(this)
    }
    addCounter(e){
        let newArr = [1, ...this.state.counter]
        this.setState({
            counter: newArr
        })
    }
    render() {
        if(this.state.counter){
            return (
                <div>
                    <button onClick={this.addCounter}>add counter</button>
                    {this.state.counter.map(e=>{
                        return(
                            <Counter count={e}/>
                        )
                    })}
                </div>
            )
        } else {
            return(
                <div>loading...</div>
            )
        }
    }
}

class Counter extends Component {
    constructor(props) {
        super(props)
        this.state = {
            count: this.props.count
        }
        this.increment = this.increment.bind(this)
        this.decrement = this.decrement.bind(this)
    }
    increment(e){
        this.setState({count: this.state.count + 1})
    }
    decrement(e){
        this.setState({count: this.state.count - 1})
    }
    render() {
        return (
            <span>
                <b onClick={this.increment}>+</b>
                <i>{this.state.count}</i>
                <b onClick={this.decrement}>-</b>
            </span>
        )
    }
}

render(<App/>, document.getElementById('root'))

1 个答案:

答案 0 :(得分:4)

主要问题不在于您将项目添加到数组中的方式,而是在呈现子组件时没有提供密钥。

渲染初始数组时会发生的情况是,子组件在数组中的每个项目都被实例化一次。但是,React无法将数组中的值映射到这些实例。

让我们调用第一个实例A.当你在列表前面再次渲染时,第一个子实例(在你的this.state.counter.map产生的数组中)仍然是实例A,只需要支持设置为新值。您可以通过例如在您孩子的渲染方法中记录this.props.e来验证这一点。在添加新项目之后,第一个记录的值应该与前置值相对应。

由于您的子组件是有状态的,并且没有做任何事情来处理componentWillReceiveProps,更改e prop将不会做任何事情来改变每个实例的先前状态。

在追加时它起作用的原因是因为已存在的实例仍然会与计数器数组中的项目进行1对1的映射,并且新项目将呈现为Counter的新实例。

例如,如果要重新排列计数器中项目的顺序,则会遇到同样的问题。生成的子实例不会改变顺序。

因此,解决方案是为每个项目提供一个唯一的Counter键。由于您的物品没有固有的身份,我的建议是提出一个

let currentId = 0
在您的App组件上方

,并使计数器数组中的每个项目都是{value,id:currentId ++}的对象,然后将id作为Key的键传递。