操纵多个子组件的状态

时间:2019-01-28 03:32:59

标签: javascript html reactjs components state

我不确定是否最好将状态保留在单个子组件或父组件中。

我有一个父组件,它将包含一个子组件,该子组件需要能够根据需要进行复制。

我有几个问题:

  • 我在哪里存储单个组件的状态?它是在组件本身中还是在父对象中?

  • 如果它在子组件中,我如何告诉父组件更新其他子组件。

  • 如果在父级中,我如何将一个函数传递给子级,以更新ITS状态而不是父级状态?

  • 如何访问每个组件状态并告诉其根据另一个子状态的更改而更改?

目前,我正在将一个新的“卡片”组件推入一个数组,该组件跟踪我需要在“板上”渲染的所有组件。

我无法概念化管理一切状态以及如何访问每个孩子的最佳方法。他们有个人ID吗?我该如何更改其所有状态。

  • ---------------------董事会----------------------- * < / li>
    import React from "react";
    import Card from "./Card";

    export default class Board extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          cards: [<Card />]
        };
      }

      componentDidMount() {}

      createCard() {
        this.state.cards.push(<Card />);
        this.forceUpdate();
      }

      render() {
        return (
          <div id="Board">
            <button onClick={() => this.createCard()}>Create Card</button>
            {this.state.cards.map(card => (
              <Card />
            ))}
          </div>
        );
      }
    }
  • --------------------------卡-------------------- ---------- *
 export default class Card extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
       active: false
     };
   }

   cardClick = () => {
     this.setState({
       active: !this.state.active
     });
   };

   render(cardClick) {
     return (
       <div>
         {this.state.active ? (
           <div
             className="activeCard"
             id="card"
             onClick={() => this.cardClick()}
           >
             Active
           </div>
         ) : (
           <div
             className="inactiveCard"
             id="card"
             onClick={() => this.cardClick()}
           >
             Inactive
           </div>
         )}
       </div>
     );
   }
 } ```



3 个答案:

答案 0 :(得分:1)

好的,让我们从顶部开始。我想您有一些要渲染为<Card />的数据,每个数据项一个。您无需将组件保持在状态中,而应将数据保持在状态中。例如:

this.state = {
  data: [
    {value: 'Cat 1'},
    {value: 'Cat 2'},
    {value: 'Cat 3'}
  ] 
} 

在这种情况下,父级保留数据是正确的。但是,lifting the state up (React docs link)的整个概念并不难。通常,这是一个问题:一个以上的组件是否在乎/取决于相同的状态?如果答案是肯定的,通常父母应该持有它。您的每张卡都保持自己的活动状态是正确的。但是,父母也可以这样做。那样做吧,那么我们将:

Board.js

import React from 'react';
import {Card} from './Card';    

class Board extends React.Component{
    state = {
      data: [
        {value: 'Cat 1'},
        {value: 'Cat 2'},
        {value: 'Cat 3'}
      ],
      activeCard: null, 
    }
    cardClick = id => {
       this.setState({ activeCard: id })
    }
    addCard = () => {
      const newCard = { value: 'Some val here' };
      this.setState({ data: [...this.state.data, newCard] });
    }
    render(){
      return(
        <div id="Board">
          <button onClick={this.addCard}>Add a card</button>
          {this.state.data.map((item, index) => 
            <Card key={index} 
              value={item.value} 
              active={this.state.activeCard === index}
              cardClick={this.cardClick}
              index={index}
            />
           )}
         </div>
       )
    }
}

export default Board;

这样做,我们的父母可以管理一切。并不是说这是唯一正确的方法,但这使我们可以将Card组件作为“哑”功能组件。请注意-这种特定方法依赖于数据永不更改顺序,因为它依赖于.map索引,索引从0到数据长度-1。

Card.js

import React from 'react';

const Card = props => (
    <div
        className={props.active ? 'activeCard' : 'inactiveCard'}
        onClick={() => props.cardClick(props.index)}
    >
        Active
    </div>
);

export { Card };

您发布的内容和我完成的操作之间存在一些关键差异。

1)您正在映射具有相同id="card"的卡片,这很糟糕,ID应该是唯一的。另外,除非您真的需要它,否则可以忽略它。

2)我根据活动卡的索引切换className。如果当前卡(例如2)也是活动卡,则它将具有activeCard className。

3)最后,我向孩子传递了一个更新父母状态的函数。通过这样做,我将状态包含在父级中,并且每次更新时,状态也会反映在子级上。这并不是说您为Cards使用基于类的组件的方法是错误的,但是我认为这很简单。

4)WAI-ARIA只是把它扔在那里,并不完全同意具有onClick个事件的div。为了使Internet成为一个凉爽,易于访问的地方,您可以在div上放置一个role="button",以表明它是一个按钮。这还要求它具有可聚焦性以及键盘事件侦听器,在这种情况下,如果元素应该是可单击的,最好使用<button>

要回答您的其他问题:

  • 父级自动将所有状态更改传播给所有关心它的子级

  • 所有子组件都独立于父组件,例如,如果它们具有自己的状态,则父组件不在乎。只有当他们共享一个状态和一个状态更新功能时,这才有意义,因此,只有当您专门将此功能传递给子级时,

  • 如果孩子共享一个州,则应将其抬高,这在链接的文档中有解释!

编辑:对于给定的示例,我假设您一次只需要一张活动卡。如果不是这种情况,要么让每张卡都保持其活动状态(如Keno Clayton建议的那样),要么将activeCard更改为数组,然后根据活动卡的数组检查每张卡的索引

答案 1 :(得分:1)

要回答您的问题:

  

我在哪里存储单个组件的状态?它是在组件本身中还是在父对象中?

最佳实践是将状态存储在需要它的组件中。如果两个兄弟组件需要访问相同的状态,则将其提升到父状态。

然后您可以使用Context Api传递单个道具或传递整个状态。如果需要,您可以使用它来传递功能以更新父状态,并且子项将在更新时自动接收道具。


对于您的特定情况,每张卡应具有自己的状态,以确定其是否处于活动状态。也可以在父级中保留一系列活动状态,但这不是那么直观,也有点复杂。

  

如何访问每个组件状态,并告诉它根据另一个子状态的变化而更改?

您不应该这样做。您应该保留要与父级中其他组件共享的任何信息。

答案 2 :(得分:0)

Where do i store the state for the individual component is it in the component itself or is it in the parent?
如果仅在该组件内控制和需要该状态,则单个组件的状态应位于该组件内。例如:Card中的active(boolean)。由于单击卡片应使其处于选中状态,并使当前活动的卡片处于非活动状态,我们可以得出结论,最好将其存储在父级的卡片组件外部。

If it's in the parent how do I pass a function to the child which will update ITS state and not the parents state?

您可以将该函数作为道具传递(绑定到父项之后)

How do I access each of the components state and tell it to change based on another child state changing?

由于您将卡片列表和活动布尔值保留在父级Board组件中,因此不需要这样做。

在董事会中保持这种状态。

this.state = {
   cards: [
     {id: 1, name: 'Card1'},
     {id: 1, name: 'Card2'},
     {id: 1, name: 'Card3'},
   ],
   activeId: 2
}