“componentDidMount”生命周期方法不更新状态

Posted

技术标签:

【中文标题】“componentDidMount”生命周期方法不更新状态【英文标题】:The 'componentDidMount' lifecycle method does not update the state 【发布时间】:2020-06-01 20:39:13 【问题描述】:

我有一个名为 DiscardPile 的组件,它在您的道具 (props.cards) 中接收一组对象,如下所示:

key: 'key', 'type', suit: 'suit', label: 'label', flipped: 'fliped, draggable: 'draggable', currentOrder: 0

嗯,DiscardPile 组件如下所示:

import React,  Component  from 'react';
import $ from 'jquery';
import 'jquery-ui/ui/core';
import 'jquery-ui/ui/widgets/droppable';

import './DiscardPile.scss';

import Card from '../Card/Card';
export default class DiscardPile extends Component 

    constructor(props) 
        super(props);
        this.state = 
            currentCards: []        
        
    

    componentDidMount() 
        let currentCards = [...this.props.cards];
        for(let i = 0; i < currentCards.length; i++) 
            currentCards[i].currentOrder = i;
        
        this.setState(currentCards: currentCards);
    

    render() 
        return (
            <div id="discard-pile" className="discard-pile">
                this.state.currentCards.map(card =>(
                    <Card key=card.key type=card.type suit=card.suit label=card.label flipped=card.flipped draggable=card.draggable currentOrder=card.currentOrder></Card>
                ))
            </div>
        )
    


'componentDidMount' 应该更新对象的 currentOrder 属性并再次呈现卡片列表,但这不会发生。事实上,列表被渲染为空。 我能做些什么来解决这个问题?

(对不起,我的英语不好)

--更新--

DiscardPile 组件由 KlondikeTable 组件调用,如下所示:

import React,  Component  from 'react';

import './KlondikeTable.scss';

import Card from './../../generalComponents/Card/Card';
import DiscardPile from '../../generalComponents/DiscardPile/DiscardPile';
import FlippedPile from '../../generalComponents/FlippedPile/FlippedPile';
import SuitsPile from '../../generalComponents/SuitsPile/SuitsPile';
import CardsColumn from '../../generalComponents/CardsColumn/CardsColumn';

export default class KlondikeTable extends Component 

    constructor(props) 
        super(props);
        this.state = 
            cards: [],
            initialDistribution: 
                discardPile: [],
                flippedPile: [],
                cardsPile1: [],
                cardsPile2: [],
                cardsPile3: [],
                cardsPile4: [],
                cardsPile5: [],
                cardsPile6: [],
                cardsPile7: [],
            ,
            currentDistribution: null
        
        this.generateCards = this.generateCards.bind(this);
        this.shuffleCards = this.shuffleCards.bind(this);
    

    componentDidMount() 
        this.generateCards();
    

    generateCards() 
        let cards = [];
        let types = ["K","Q","J","10","9","8","7","6","5","4","3","2","A"];
        let suits = ["spades", "clubs", "diamonds", "hearts"];
        for(let i = 0; i < suits.length; i++) 
            for(let j = 0; j < types.length; j++) 
                cards.push(
                    key: types[j] + "-" + suits[i] + "-0", type: types[j], suit: suits[i], label: "0", flipped: false, draggable: true, currentOrder: 0
                )
            
        
        this.setState(state => (...state, cards: cards), () => 
            this.shuffleCards();
        );
    

    shuffleCards() 
        let amount = 52;
        let numbersArray = [];
        let cards = [...this.state.cards];
        let initialDistribution = ...this.state.initialDistribution;
        for(let i = 0; i < 52; i++) 
            numbersArray.push(i);
        
        for(let i = 0; i < 24; i++) 
            let randomNumber = Math.floor(Math.random() * amount);
            let removedElement = cards.splice(randomNumber, 1)[0];
            initialDistribution.discardPile.push(removedElement);
            amount--;
        
        for(let i = 0; i < 28; i++) 
            let randomNumber = Math.floor(Math.random() * amount);
            let removedElement = cards.splice(randomNumber, 1)[0];
            if(i < 1) 
                initialDistribution.cardsPile1.push(removedElement);
            else if(i < 3) 
                initialDistribution.cardsPile2.push(removedElement);
            else if(i < 6) 
                initialDistribution.cardsPile3.push(removedElement);
            else if(i < 10) 
                initialDistribution.cardsPile4.push(removedElement);
            else if(i < 15) 
                initialDistribution.cardsPile5.push(removedElement);
            else if(i < 21) 
                initialDistribution.cardsPile6.push(removedElement);
            else if(i >= 21) 
                initialDistribution.cardsPile7.push(removedElement);
            
            amount--;
        
        this.setState(initialDistribution: initialDistribution, currentDistribution: initialDistribution);
    

    render() 
        return (
            <div className="klondike-table">
                <DiscardPile cards=this.state.initialDistribution.discardPile></DiscardPile>
                <FlippedPile cards=this.state.initialDistribution.flippedPile></FlippedPile>
                <div className="suits-piles-container">
                    <SuitsPile></SuitsPile>
                    <SuitsPile></SuitsPile>
                    <SuitsPile></SuitsPile>
                    <SuitsPile></SuitsPile>
                </div>
                <CardsColumn cards=this.state.initialDistribution.cardsPile1></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile2></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile3></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile4></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile5></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile6></CardsColumn>
                <CardsColumn cards=this.state.initialDistribution.cardsPile7></CardsColumn>                
            </div>
        )
    


【问题讨论】:

@Dexygen 在这里完全不相关。 你确定this.props.cards 的长度总是 > 0 吗?如果添加到 componentDidMount console.log(this.props.cards.length) 会得到什么? 要么你没有在你的帖子中包含它,要么你忘记为你的状态定义接口定义。 @Chris,请检查更新。我把控制台日志显示为 0。是因为在 KlondikeComponent 中我在 componentDidMount 中定义了数组(在渲染后调用)? 【参考方案1】:

首先,您不需要拥有 currentCards 状态元素。如果你需要你的 state 元素总是用你的 props 元素内容更新,你可以这样想,你可能会认为它是错误的。此外,如果你的 state 元素只是你在 props 中收到的元素的轻微变化,那么你肯定不需要它。 您的代码不起作用,因为在第一次渲染时,您的组件接收到一个空数组,因此设置了一个空的 currentCards 数组并且永远不会更改它。

随便用

render() 
    return (
        <div id="discard-pile" className="discard-pile">
            this.props.cards.map((card, currentOrder)=>(
                <Card key=card.key type=card.type suit=card.suit label=card.label flipped=card.flipped draggable=card.draggable currentOrder=currentOrder></Card>
            ))
        </div>
    )
` 

达到预期的行为。

如果你想要的只是在你的状态下拥有 props.cards 的初始版本,你可能想要使用 componentDidUpdate 甚至 shouldComponentUpdate 以及一组明确定义的规则,以避免性能损失或无限重新渲染。

【讨论】:

【参考方案2】:

由于componentDidMount 只运行一次,如果prop 稍后发生更改,您所做的任何更改都不会生效。

属性cards 最初设置为[]。因此,除非您将排序逻辑移到其他地方,否则它永远不会更新状态以包含您更新的数组。

在不知道你想要的确切逻辑的情况下,这样的事情可能会起作用:

componentDidUpdate(prevProps) 
  if (prevProps.cards.length !== this.props.cards.length) 
    let currentCards = [...this.props.cards];
    for(let i = 0; i < currentCards.length; i++) 
      currentCards[i].currentOrder = i;
    
    this.setState(currentCards: currentCards);
  

这将仅在 cards 数组的长度发生变化时更新状态。

【讨论】:

谢谢!我的逻辑有点不同,但它让我意识到我必须大量研究 React 生命周期。

以上是关于“componentDidMount”生命周期方法不更新状态的主要内容,如果未能解决你的问题,请参考以下文章

React生命周期

Apollo + React:数据未出现在 componentDidMount 生命周期中

react的生命周期

RN 生命周期

React Component 不支持生命周期方法?

react生命周期详解