React Component的状态与.unshift()呈现出奇怪的异常,但与.push()一样正常 [英] React Component's state renders strangely with .unshift(), but normal with .push()

查看:157
本文介绍了React Component的状态与.unshift()呈现出奇怪的异常,但与.push()一样正常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an array, I have 2 components(child and parent). I iterate through array within parent component, I render child components, I give them props with data from array.

子组件将其道具转换为状态然后具有增量和减少该状态。

Child components have their props translated to state and then have increment and decrement that state.

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

Parent component can add new item into array and re-render. BUT. If i unshift() new item in front of the array, i get last item from array added to the screen instead of new one to the front.

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

QUESTION: Why it renders good with .push() and bad with .unshift(). Also everything is ok with concat and [newItem, ...oldArray], but bad with same things when i add items in front of the array? Also how to properly .unshift() new items(comments, counters, images, posts, eg anything) into state, so they render first?

PS:我做的任何事情(concat,slice,... array,unshift,react's immutability helper)似乎无法正常工作。 Mobx和Redux没有帮助。

PS: Anything i do (concat, slice, ...array, unshift, react's immutability helper) doesn't seem to work properly. Mobx and Redux didn't help.

PS:这也发生在Mithril,Inferno和Aurelia。

PS: This also occurs with Mithril, Inferno and 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'))


推荐答案

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

The main problem isn't the way you are prepending items to your array, it's that you are not providing a key when rendering the child component.

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

What happens when you render the initial array is that the child component gets instantiated once per item in your array. However, React has no way of mapping the values in your array to those instances.

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

Let's call the first instance A. When you prepend to your list and render again, the first child instance (in the array resulting from your this.state.counter.map) will still be instance A, just with the prop e set to a new value. You can verify this by for example logging this.props.e in your child's render method. After prepending the new item, the first logged value should correspond to the prepended value.

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

Since your child component is stateful, and does not do anything to handle componentWillReceiveProps, having the e prop changed will not do anything to change each instance's previous state.

在追加时它的工作原因是因为已存在的实例仍将映射为1到1包含计数器数组中的项目,新项目将呈现为计数器的新实例。

The reason why it works when you append is because the already existing instances will still map 1-to-1 with the items in your counter array, and the new item will be rendered as a new instance of Counter.

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

You would have the same problem if you were to rearrange the order of the items in counter, for example. The resulting child instances would not change order.

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

So, the solution is to provide a unique key to Counter, for each item. Since your items do not have an intrinsic identity, my suggestion would be to put a

let currentId = 0

在您的App组件上方,并且您的计数器数组中的每个项目都是{value,id:currentId ++}的对象,然后传递id作为Counter的键。

above your App component, and have each item in your counter array be an object of {value, id: currentId++}, then pass id as the key to Counter.

这篇关于React Component的状态与.unshift()呈现出奇怪的异常,但与.push()一样正常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆