简体   繁体   English

谁能解释为什么这适用于 class 组件而不是功能组件?

[英]Can anyone explain why this works with a class component but not a functional component?

Basically, I have several buttons.基本上,我有几个按钮。 I want the user to be able to have multiple buttons selected.我希望用户能够选择多个按钮。

I tried using a functional component and storing the button state as an object with the useState hook.我尝试使用功能组件并将按钮 state 存储为带有 useState 挂钩的 object。 The state updates accordingly when the buttons are clicked, but the button's props do not update.当点击按钮时,state 会相应更新,但按钮的道具不会更新。 I've tried useEffect to rerender the component when props.isActive changes but that did not work.当 props.isActive 发生变化时,我尝试使用 useEffect 重新渲染组件,但这不起作用。

Using a class component, this works exactly as intended.使用 class 组件,这完全符合预期。 I'm just trying to understand why that is the case.我只是想了解为什么会这样。 If anyone can provide insight, I would greatly appreciate it.如果有人能提供见解,我将不胜感激。 Thanks.谢谢。

Functional Component功能组件

const View = (props) => {
  var [buttons, setButtons] = useState([
    { name: "Small", isActive: false },
    { name: "Large", isActive: false },
  ]);
  const handleClick = (index) => {
    let tmp = buttons;
    tmp[index].isActive = !tmp[index].isActive;
    return setButtons(tmp);
  };
  return (
      <div>
            {buttons.map((e, index) => {
              return (
                <MyButtonComponent
                  key={index}
                  name={e.name}
                  isActive={e.isActive}
                  onClick={() => handleClick(index)}
                />
              );
            })}
    </div>
  );
};

Class Component Class 组件

class View extends Component {
  state = {
    btn: [
      { name: "Small", isActive: false },
      { name: "Large", isActive: false },
    ],
  };
  handleClick = (index) => {
    let tmp = this.state.btn;
    tmp[index].isActive = !tmp[index].isActive;
    return this.setState({ ...this.state, btn: tmp });
  };
  render() {
    return (
            <div>
              {this.state.btn.map((e, index) => {
                return (
                  <MyButtonComponent
                    key={index}
                    name={e.name}
                    isActive={e.isActive}
                    onClick={() => this.handleClick(index)}
                  />
                );
              })}
            </div>
    );
  }
}

You are mutating your old array, and then setting state with the mutated array.您正在对旧数组进行变异,然后使用变异数组设置 state 。 This is not a good idea in react, regardless of whether you're using class components or function components.无论您使用的是 class 组件还是 function 组件,这在反应中都不是一个好主意。 The class component let you get away with it, but the function component compares the state before with the sate after and sees that they're the same array, so it skips rendering. class 组件让您侥幸逃脱,但 function 组件将 state 之前的状态与之后的状态进行比较,并看到它们是相同的渲染数组。

To fix this, you should create a new state instead of mutating the old.要解决此问题,您应该创建一个新的 state 而不是改变旧的。 Change this:改变这个:

let tmp = buttons;
tmp[index].isActive = !tmp[index].isActive;
return setButtons(tmp);

To this:对此:

// Create a copy of the array
let tmp = [...buttons]; 
// Also copy the item you want to change
tmp[index] = {
  ...tmp[index],
  active: !tmp[index].active
}
setState(tmp);

You're updating the reference and setting the same reference to the state (setButtons(tmp)) , which react thinks , array didn't change because of shallow comparison.您正在更新引用并将相同的引用设置为 state (setButtons(tmp)) ,它react thinks ,由于浅比较,数组没有改变。 You need use new reference.您需要使用新的参考。 Like the following像下面这样

    let tmp = buttons; <-- problem is here, reference 
    tmp[index].isActive = !tmp[index].isActive;
    return setButtons(tmp); <-- and updating same `reference`
   const handleClick = (index) => {
     buttons[index].isActive = !buttons[index].isActive;
     return setButtons([...buttons]); <-- this will work
   };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM