【问题标题】:React: multiple components with the same event handler: trying to change the state of only the component which was clicked and not of all the othersReact:具有相同事件处理程序的多个组件:尝试仅更改被单击的组件的状态,而不是所有其他组件的状态
【发布时间】:2021-08-01 20:54:40
【问题描述】:

我是 React 新手,必须处理一项特定任务,其中我的应用程序的所有逻辑都在一个父组件中,而子组件只接收一些道具。但是,在孩子身上不应该有任何逻辑或几乎没有逻辑。 我有一个由 25 个单元组成的网格(父组件),每个单元(子组件)可以打开或关闭。将每个单元想象成一盏开或关的灯。 从我的父组件中,我将单元组件渲染 25 次。每次每个单元格都有:

  1. 一把钥匙
  2. 身份证
  3. 一种状态(随机分配的开启或关闭)
  4. 点击事件

在我的子组件中,当点击事件被触发时,子组件返回给父组件它的 id 和它的状态(开或关)

我想要达到的目标:

在我的父组件中,我希望能够检测到哪个孩子被点击,并且只改变被点击孩子的状态。

到目前为止我得到了什么:

尽管父母收到了被点击的孩子的id和状态,当我通过setState改变状态时,所有的孩子都会受到影响。

这是我的父组件的 sn-p:

import React, { Component } from 'react';
import GridSquare from './GridSquare';
import { randomlyLit, cellIdentifier } from '../helpers/helperFuns.js';
let nums = []
cellIdentifier(nums, 25)

class GridContainer extends Component {
  static defaultProps = {
    gridSize: 25
    
  };
  constructor(props) {
    super();
    this.state = {
     cellID: nums,
      hasWon: false,
      lightStatus: Array.from({ length: 25 }, () => randomlyLit()),
    };
    this.changeValue = this.changeValue.bind(this);
  }

  
  changeValue(id, value) {
  console.log(id, value);
  this.setState(st => ({
    // let result = st.cellID.filter(c => c===id)
    // if(result){
    //   st.value = !value;
    // } 
    lightStatus : !value

  })
    
  )
   
  }

  render() {
  
    return (
      <div>
        <h1 className="neon">
          Light <span className="flux">Out</span>
        </h1>
        <div className="GridContainer">
        {this.state.cellID.map((el, i) =>(
          <GridSquare key={this.state.cellID[i]} id={this.state.cellID[i]} lit={this.state.lightStatus[i]} click={this.changeValue}/>
        ))}
        </div>
      </div>
    );
  }
}

export default GridContainer;

这是我的子组件的 sn-p:

import React, {Component} from 'react';

class GridSquare extends Component {
    
    constructor(props){
        super(props);
       
        this.handleClick= this.handleClick.bind(this)
    }

    handleClick(){
        this.props.click( this.props.id, this.props.lit);
    
    }

    render() {
        const squareClasses = {
            'GridSquareOn': this.props.lit === true,
            'GridSquareOff': this.props.lit === false,
            'GridSquare': true,
        }

        function classNames(squareClasses) {
            return Object.entries(squareClasses)
              .filter(([key, value]) => value)
              .map(([key, value]) => key)
              .join(' ');
          }

        const myClassName = classNames(squareClasses)
       
       
        return(
           <div className={myClassName}  onClick={this.handleClick}>
           </div>
        )
    }
}

export default GridSquare;

我的 app.js 只渲染父组件,没有别的:

import GridContainer from './components/GridContainer.jsx'

import './style/App.css';
import './style/GridContainer.css';
import './style/GridSquare.css';

function App() {
  return (
    <div className="App">
     
     <GridContainer />
    </div>
  );
}

export default App;

提前感谢您的帮助!

【问题讨论】:

  • 看看Event.target
  • @Detonar。感谢您的关注,但我现在正在研究您的建议,但我不知道如何使用它。如果我将 evt 记录到控制台,我看不到任何相关的内容可能有助于我仅更改单击组件的状态。也许我很愚蠢。
  • Event.target 始终包含引发事件的元素。因此,通过阅读它,您可以检查此事件的值。例如,event.target.id 将始终包含引发事件的元素的 id

标签: javascript reactjs methods components


【解决方案1】:
changeValue(id, value) {
  console.log(id, value);
  this.setState(st => ({
    // let result = st.cellID.filter(c => c===id)
    // if(result){
    //   st.value = !value;
    // } 
    lightStatus : !value

  })

您的更改值函数正在使用错误的值更新状态。 lightStatus 的初始值是一个布尔数组(假设 randomlyLit 函数将返回一个布尔值。

当您单击其中一个单元格时,lightStatus 会更新为 false 的值而不是数组。

要解决此问题,请按单击单元格的索引搜索整个 lightStatus 数组,并使用 Array.slice 更新该特定索引处的布尔值。

如何优化

而不是每次遍历整个数组来更新单元格的lightStatus。您可以将值保存在 Object 中。

如果我可以像这样更新changeValue 中的状态会怎样?

this.setState((st) => {
  return {
    ...st,
    lightStatus: { ...st.lightStatus, [id]: value } // Direct update without traversing through array
  }
});

lightStatus 可用于在cellID 中的单元格 id 与其对应的状态布尔值之间形成“映射”。

【讨论】:

  • 非常感谢您!你真的让我看到了光明 ;-) 这就是我在你回答后更改代码的方式:``` changeValue(index, value) { let flippedValue = !value;让 newLightStatus = [...this.state.lightStatus]; newLightStatus.splice(index,1,flippedValue); this.setState(st => st.lightStatus = newLightStatus) } ```
  • @Emmanuel 没错。尽管这会起作用,但您始终可以对其进行优化,以免每次必须切换一盏灯时遍历整个阵列。 ;) 可能是一个好习惯。
  • 如果我在利用,请原谅我,但是由于您的第一个建议对我有很大帮助,请您“只是”指出我如何在不遍历整个文件的情况下优化它的方向吗?大批?提前致谢
  • 非常感谢您的好意...我会努力实现您的想法!非常感谢
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 2017-01-08
  • 2016-05-29
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 2021-05-11
  • 2021-08-09
相关资源
最近更新 更多