【问题标题】:Couldn't correctly initialize state in parent component from children states无法从子状态正确初始化父组件中的状态
【发布时间】:2020-10-30 15:39:32
【问题描述】:

我有两个 React 组件,即Form 和SimpleCheckbox。 SimpleCheckbox 使用了一些 Material UI 组件,但我相信它们与我的问题无关。

在Form 中,useEffect 调用api.getCategoryNames(),它解析为一个类别数组,例如['Information', 'Investigation', 'Transaction', 'Pain']。

我的目标是在父组件 (Form) 中访问复选框的状态(选中或未选中)。我已采用this question 中建议的方法。(请参阅经过验证的答案)

有趣的是,当我记录 checks 时,它给出了(在 api 调用解决之后):

{Pain: false}

我的期望是:

{
  Information: false,
  Investigation: false,
  Transaction: false,
  Pain: false,
}

此外,当我点击复选框时,checks 状态会正确更新。例如,假设我检查了Information 和Investigation 框,check 变为以下内容:

{
  Pain: false,
  Information: true,
  Investigation: true,
}

这里是组件:

const Form = () => {
  const [checks, setChecks] = useState({});
  const [categories, setCategories] = useState([]);

  const handleCheckChange = (isChecked, category) => {
    setChecks({ ...checks, [category]: isChecked });
  }

  useEffect(() => {
    api
      .getCategoryNames()
      .then((_categories) => {
        setCategories(_categories);
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  return (
    {categories.map(category => {
       <SimpleCheckbox 
         label={category}
         onCheck={handleCheckChange}
         key={category}
         id={category}
       />
    }
  )
}

const SimpleCheckbox = ({ onCheck, label, id }) => {
  const [check, setCheck] = useState(false);

  const handleChange = (event) => {
    setCheck(event.target.checked);
  };

  useEffect(() => {
    onCheck(check, id);
  }, [check]);

  return (
    <FormControl>
      <FormControlLabel
        control={
          <Checkbox checked={check} onChange={handleChange} color="primary" />
        }
        label={label}
      />
    </FormControl>
  );
}

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    看起来您在表单级别和复选框组件级别控制了两次状态。

    我消除了其中一种状态并更改了处理程序。此外,我将检查设置为具有 initialState,这样您就不会收到 uncontrolled to controlled input warning

    import React, { useState, useEffect } from "react";
    import { FormControl, FormControlLabel, Checkbox } from "@material-ui/core";
    import "./styles.css";
    
    export default function App() {
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
          <Form />
        </div>
      );
    }
    
    const Form = () => {
      const [checks, setChecks] = useState({
        Information: false,
        Investigation: false,
        Transaction: false,
        Pain: false
      });
      const [categories, setCategories] = useState([]);
      console.log("checks", checks);
      console.log("categories", categories);
      const handleCheckChange = (isChecked, category) => {
        setChecks({ ...checks, [category]: isChecked });
      };
    
      useEffect(() => {
        // api
        //   .getCategoryNames()
        //   .then(_categories => {
        //     setCategories(_categories);
        //   })
        //   .catch(error => {
        //     console.log(error);
        //   });
        setCategories(["Information", "Investigation", "Transaction", "Pain"]);
       
      }, []);
    
      return (
        <>
          {categories.map(category => (
            <SimpleCheckbox
              label={category}
              onCheck={handleCheckChange}
              key={category}
              id={category}
              check={checks[category]}
            />
          ))}
        </>
      );
    };
    
    const SimpleCheckbox = ({ onCheck, label, check }) => {
      return (
        <FormControl>
          <FormControlLabel
            control={
              <Checkbox
                checked={check}
                onChange={() => onCheck(!check, label)}
                color="primary"
              />
            }
            label={label}
          />
        </FormControl>
      );
    };
    

    如果您希望通过 api 动态提供检查,您可以编写一个 fetchHandler 来等待 api 的结果并更新两个状态片

      const fetchChecks = async () => {
        let categoriesFromAPI = ["Information", "Investigation", "Transaction", "Pain"] // api result needs await
        setCategories(categoriesFromAPI);
        let initialChecks = categoriesFromAPI.reduce((acc, cur) => {
          acc[cur] = false
          return acc
        }, {})
        setChecks(initialChecks)
       
      }
      useEffect(() => {
        fetchChecks()
      }, []);
    

    我对 categoriesFromApi 变量进行了硬编码,请确保在您的 api 调用语句前添加 await。

    let categoriesFromApi = await axios.get(url)
    

    最后,将初始状态切片设置为空对象

    const [checks, setChecks] = useState({});
    

    【讨论】:

    • 你好@Hyetigran,我真的不能像你硬编码那样初始化checks状态,因为API的响应可能会改变。
    • @CagriUysal 现在怎么样?
    【解决方案2】:

    我缺少的是在setChecks 中使用功能更新。 Hooks API Reference 说:If the new state is computed using the previous state, you can pass a function to setState.

    所以改变后:

    const handleCheckChange = (isChecked, category) => {
        setChecks({ ...checks, [category]: isChecked });
      }
    
    

    到

    const handleCheckChange = (isChecked, category) => {
        setChecks(prevChecks => { ...prevChecks, [category]: isChecked });
      }
    

    它已经开始按我的预期工作了。

    【讨论】:

      猜你喜欢
      • 2015-05-10
      • 2020-08-30
      • 2023-03-08
      • 1970-01-01
      • 2021-06-27
      • 1970-01-01
      • 2019-05-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多