简体   繁体   English

为什么将更新的 state 从子组件提升到父组件不起作用?

[英]Why lifting an updated state from a child to a parent component does not work?

I managed to reach the result I wanted when trying to update the UI of a parent component after toggling the state of a child, however, as a React beginner I guess I haven't done it quite right.在切换孩子的 state 后尝试更新父组件的 UI 时,我设法达到了我想要的结果,但是,作为 React 初学者,我想我做得不太对。

As you will see in the code below, I updated the state of my radio button components based on event.target.value and then I lifted it to the component using SitePageToggle ( WebsitePicker ) via the onToggle prop.正如您将在下面的代码中看到的,我根据event.target.value更新了单选按钮组件的 state,然后通过onToggle道具使用SitePageToggle ( WebsitePicker ) 将其提升到组件。 The problem is that, in my understanding, I should have used selectedRadioBtn with its updated state. However, when I pass selectedRadioBtn so that I can base the state of my QAObject variable according to it, the toggling order between 'Website' and 'Page' is messed up and inverted.问题是,根据我的理解,我应该使用selectedRadioBtn及其更新的 state。但是,当我通过selectedRadioBtn以便我可以根据它来确定我的QAObject变量的 state 时,“网站”和“页面”之间的切换顺序' 被搞砸了和颠倒了。 If I use the updated state in the child component itself this isn't a problem.如果我在子组件本身中使用更新的 state,这不是问题。

I know that [state updates may be asynchronous][1], but using the useState hook second version by accepting a callback hasn't solved the problem either.我知道[状态更新可能是异步的][1],但是通过接受回调使用 useState 钩子第二个版本也没有解决问题。

Here's the code in the parent component :这是父组件中的代码:

import styles from "./WebsitePicker.module.css";
import { useState } from "react";
import SitePageToggle from "./SitePageToggle";
import SubmitButton from "./Buttons/SubmitButton";

function WebsitePicker() {
    const [QAObject, setQAObject] = useState("Website");
    const { label, input__url } = styles;

    const toggleWebsitePageHandler = toggleData => {
        if (toggleData === 'Website') {
            setQAObject("Website");
        } else {
            setQAObject("Page");
        }
    };

    return (
        <>
            <SitePageToggle onToggle={toggleWebsitePageHandler} />
            <form onSubmit={formSubmissionHandler}>
                <label className={label} htmlFor="picker">
                    Enter the URL of the {QAObject} you wish to QA.
                </label>
                <br />
                <input
                    id="picker"
                    type="url"
                    className={input__url}
                />
                <SubmitButton>QA my page</SubmitButton>
            </form>
        </>
    );
}

export default WebsitePicker;

And here's the code in the child component :这是子组件中的代码:

import RadioButton from "./Buttons/RadioButton";

function SitePageToggle({ onToggle }) {
    const [selectedRadioBtn, setSelectedRadioBtn] = useState("Website");

    const isRadioSelected = (btnValue) => selectedRadioBtn === btnValue;

    const handleToggle = event => {
        setSelectedRadioBtn(event.target.value);
        onToggle(event.target.value);
    };

return (
    <ul>
        <li>
            <RadioButton
                label="Website"
                name="react-radio-btn"
                value="Website"
                checked={isRadioSelected("Website")}
                onChange={handleToggle}
            />
        </li>
        <li>
            <RadioButton
                label="Page"
                name="react-radio-btn"
                value="Page"
                checked={isRadioSelected("Page")}
                onChange={handleToggle}
            />
        </li>
    </ul>
);
}

export default SitePageToggle;


  [1]: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Not sure if I fully understand the use case, but if the goal is to lift the selectedRadioBtn state to the parent (instead of syncing two states), then perhaps SitePageToggle does not need to keep a separate copy of the state, rather receive it from the parent.不确定我是否完全理解用例,但如果目标是将selectedRadioBtn state提升到父级(而不是同步两个状态),那么SitePageToggle可能不需要保留 state 的单独副本,而是从父母。

Quick simplified demo on: stackblitz快速简化演示: stackblitz

In WebsitePicker pass QAObject as selectedRadioBtn :WebsitePicker QAObject作为selectedRadioBtn传递:

<SitePageToggle
  selectedRadioBtn={QAObject}
  onToggle={toggleWebsitePageHandler}
/>

In SitePageToggle use selectedRadioBtn from props as the state:SitePageToggle ,使用道具中的selectedRadioBtn作为 state:

import RadioButton from "./Buttons/RadioButton";

function SitePageToggle({ selectedRadioBtn, onToggle }) {
  const isRadioSelected = (btnValue) => selectedRadioBtn === btnValue;
  const handleToggle = (event) => {
    onToggle(event.target.value);
  };

  return (
    <ul>
      <li>
        <RadioButton
          label="Website"
          name="react-radio-btn"
          value="Website"
          checked={isRadioSelected("Website")}
          onChange={handleToggle}
        />
      </li>
      <li>
        <RadioButton
          label="Page"
          name="react-radio-btn"
          value="Page"
          checked={isRadioSelected("Page")}
          onChange={handleToggle}
        />
      </li>
    </ul>
  );
}

export default SitePageToggle;

To properly pass up the child state selectedRadioBtn you should use a useEffect().要正确传递子项 state selectedRadioBtn ,您应该使用 useEffect()。 The second argument of the useEffect is the dependency array, so whenever selectedRadioBtn changes, it will fire. useEffect 的第二个参数是依赖数组,所以只要selectedRadioBtn发生变化,它就会触发。

import RadioButton from "./Buttons/RadioButton";

function SitePageToggle({ onToggle }) {
    const [selectedRadioBtn, setSelectedRadioBtn] = useState("Website");

    const isRadioSelected = (btnValue) => selectedRadioBtn === btnValue;

    const handleToggle = event => {
        setSelectedRadioBtn(event.target.value);
        // onToggle(event.target.value); <-- delete this here
    };

    useEffect(() => {
        onToggle(selectedRadioBtn);
    }, [selectedRadioBtn]);

    return (
        <>...</>
    );
}

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

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