无法从子状态正确初始化父组件中的状态

时间:2020-07-09 23:16:42

标签: javascript reactjs

我有两个React组件,分别是FormSimpleCheckboxSimpleCheckbox使用了一些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状态会正确更新。例如,假设我已选中InformationInvestigation框,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>
  );
}

2 个答案:

答案 0 :(得分: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()
  }, []);

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

let categoriesFromApi = await axios.get(url)

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

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

答案 1 :(得分:0)

我所缺少的是在<Jenkins_URL>/job/<job_name>/config.xml中使用功能更新。 Hooks API Reference说:setChecks

因此更改后:

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 });
  }

它已开始按预期工作。

相关问题