使用 Child /Parent 组件反应受控表单

React Controlled Form with Child /Parent component

我正在构建一个带有动态字段的受控表单。 Parent 组件从 redux 存储中获取数据,然后使用这些值设置状态。 我不想让它有太多的代码行,所以我把动态字段变成了一个组件。 状态保留在 parent 组件中,我使用 props 传递 handlechange 函数。

Parent :

function EditAbout(props) {
  const [img, setImg] = useState("");
  const [body, setBody] = useState(props.about.body);
  const [instagram, setInstagram] = useState(props.about.links.instagram);
  const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
  const [press, setPress] = useState(props.about.press)

  const handleSubmit = (e) => {
   // Submit the change to redux
  };

// set states with redux store
  useEffect(() => {
    setBody(props.about.body);
    setInstagram(props.about.links.instagram);
    setLinkedIn(props.about.links.linkedin);
    setPress(props.about.press);
  }, []);

  const handleChangeChild = (e, index) =>  {
    e.preventDefault();
    let articles = press
    const {value, name } = e.target
    if (name === "title") {
      articles[index].title = value;
    } else {
      articles[index].link = value;
    }
    setPress(articles)
    console.log(articles[index])
  }

  return (
    <Box>
      <h1>CHANGE ABOUT ME</h1>
      <Input
        label="Image"
        name="img"
        type="file"
        variant="outlined"
        margin="normal"
        onChange={(e) => setImg(e.target.files)}
      />
      <Input
        label="body"
        value={body}
        name="body"
        onChange={(e) => setBody(e.target.value)}
        variant="outlined"
        multiline
        rowsMax={12}
        margin="normal"
      />
      <Input
        label="instagram"
        value={instagram}
        name="instagram"
        variant="outlined"
        margin="normal"
        onChange={(e) => setInstagram(e.target.value)}
      />
      <Input
        label="Linkedin"
        value={linkedin}
        name="linkedin"
        variant="outlined"
        margin="normal"
        onChange={(e) => setLinkedIn(e.target.value)}
      />
      <Child press={press} onChange={handleChangeChild} />
      {props.loading ? (
        <CircularProgress color="black" />
      ) : (
        <Button onClick={handleSubmit} variant="contained">
          Send
        </Button>
      )}
    </Box>
  );
}

Child :

function Child(props) {
  const { press, onChange } = props;

  const inputsMarkup = () =>
    press.map((article, index) => (
      <div key={`press${index}`} style={{ display: "flex" }}>
        <input
          name="title"
          value={press[index].title}
          onChange={(e) => onChange(e, index)}
        />
        <input
          name="link"
          value={press[index].link}
          onChange={(e) => onChange(e, index)}
        />
        <button>Delete</button>
      </div>
    ));

  return (
    <div>
      <h1>Press :</h1>
      {inputsMarkup()}
    </div>
  );
}

当我输入 Parent 输入时一切正常。但是,当我使用 Child 字段对一个字符进行状态更新时,但之后又返回到之前的状态。 它也不显示字符更改。我只能在控制台中看到它。 预先感谢您的帮助

问题是您正在直接改变状态。当您创建 articles 变量 (let articles = press) 时,您实际上并没有创建一个副本,并且 articles 实际上并不包含该值。它只是对该值的引用,它指向对象在内存中的位置。

因此,当您在 handleChangeChild 函数中更新 articles[index].title 时,您实际上也在更改 press 状态。您可能认为这很好,但如果不调用 setPress(),React 将不会意识到更改。所以,虽然状态值改变了,但你不会看到它,因为 React 不会重新渲染它。

您需要使用 .map() 创建 press 数组的副本,并创建更新后的数组元素的副本。您可以在下面找到更新后的 handleChangeChild()

const handleChangeChild = (e, index) => {
  e.preventDefault();

  const { value, name } = e.target;

  setPress(
    // .map() returns a new array
    press.map((item, i) => {
      // if the current item is not the one we need to update, just return it
      if (i !== index) {
        return item;
      }

      // create a new object by copying the item
      const updatedItem = {
        ...item,
      };

      // we can safely update the properties now it won't affect the state
      if (name === 'title') {
        updatedItem.title = value;
      } else {
        updatedItem.link = value;
      }

      return updatedItem;
    }),
  );
};