【问题标题】:onChange event doesn't fire on first inputonChange 事件不会在第一次输入时触发
【发布时间】:2020-11-22 11:09:54
【问题描述】:

我是 React 新手,我有这个注册(父组件),它有一个库存(子组件),一旦用户输入他们的项目数量,我就会更改库存状态留着让我注册他们

但是 onChange 似乎被延迟了,例如,我输入了 4 个水瓶,它控制台记录了我的默认值,只有当我输入另一个项目(如食物)的数量时,它才会显示 4 个水瓶和 0 个食物:(

这就是我正在使用的...

子组件:

import React from "react";
import { Col, Row, FormGroup, Label, Input } from "reactstrap";

import waterIcon from "./../assets/water.png";
import soupIcon from "./../assets/food.png";
import medsIcon from "./../assets/aid.png";
import weaponIcon from "./../assets/gun.png";

function Inventory({ onSubmitInventory, currentInventory }) {
  const onChangeWater = (e) => {
    const newInventory = { ...currentInventory, water: e.target.value };
    onSubmitInventory(newInventory);
  };

  const onChangeSoup = (e) => {
    const newInventory = { ...currentInventory, soup: e.target.value };
    onSubmitInventory(newInventory);
  };

  const onChangeMeds = (e) => {
    const newInventory = { ...currentInventory, meds: e.target.value };
    onSubmitInventory(newInventory);
  };

  const onChangeWeapon = (e) => {
    const newInventory = { ...currentInventory, weapon: e.target.value };
    onSubmitInventory(newInventory);
  };

  return (
    <FormGroup>
      <Row className="justify-content-center text-center border-top pt-3">
        <Col xs="3">
          <img src={waterIcon} alt="Water" />
          <Label for="inventoryWater">Fiji Water:</Label>
          <Input
            type="number"
            name="inventoryWater"
            id="inventoryWater"
            placeholder="Enter amount..."
            onChange={onChangeWater}
          />
        </Col>
        <Col xs="3">
          <img src={soupIcon} alt="Soup" />
          <Label for="inventorySoup">Campbell Soup:</Label>
          <Input
            type="number"
            name="inventorySoup"
            id="inventorySoup"
            placeholder="Enter amount..."
            onChange={onChangeSoup}
          />
        </Col>
        <Col xs="3">
          <img src={medsIcon} alt="Aid" />
          <Label for="inventoryMeds">First Aid Pouch:</Label>
          <Input
            type="number"
            name="inventoryMeds"
            id="inventoryMeds"
            placeholder="Enter amount..."
            onChange={onChangeMeds}
          />
        </Col>
        <Col xs="3">
          <img
            className="d-block"
            style={{ margin: "0 auto" }}
            src={weaponIcon}
            alt="Gun"
          />
          <Label for="inventoryWeapon">AK47:</Label>
          <Input
            type="number"
            name="inventoryWeapon"
            id="inventoryWeapon"
            placeholder="Enter amount..."
            onChange={onChangeWeapon}
          />
        </Col>
      </Row>
    </FormGroup>
  );
}

export default Inventory;

父组件:

import React, { useState } from "react";
import { Col, Row, Button, Form, Label, Input } from "reactstrap";
import { useForm } from "react-hook-form";

import Inventory from "./Inventory";
import MapContainer from "./MapContainer";

function Register() {
  const [lonlat, setLonlat] = useState("");
  const onMarkerChange = (lonlat) => {
    setLonlat(lonlat);
  };

  const [inventory, setInventory] = useState({
    water: 0,
    soup: 0,
    meds: 0,
    weapon: 0,
  });
  const onSubmitInventory = (newInventory) => {
    setInventory(newInventory);
    console.log(inventory);
  };

  const { register, handleSubmit, errors } = useForm();
  const onSubmit = (data) => {
    console.log(inventory);
    const requestOptions = {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: data.personName }),
    };
    console.log(data);
  };

  return (
    <Form
      id="registerForm"
      name="registerForm"
      onSubmit={handleSubmit(onSubmit)}
    >
      <h4>New Person</h4>
      <Row className="border-top pt-4">
        <Col xs="8">
          <Label for="personName">Your name:</Label>
          <Input
            className="gray-input"
            type="text"
            name="name"
            id="personName"
            placeholder="Enter your name here..."
            innerRef={register}
          />
        </Col>
        <Col xs="2" className="text-center">
          <Label for="personAge">Age:</Label>
          <Input
            className="gray-input"
            type="number"
            name="age"
            id="personAge"
            placeholder="Enter age..."
            innerRef={register}
          />
        </Col>
        <Col xs="2" className="text-center">
          <Label for="personGender">Gender:</Label>
          <Input
            type="select"
            name="gender"
            id="personGender"
            innerRef={register}
          >
            <option defaultValue disabled>
              -
            </option>
            <option>F</option>
            <option>M</option>
          </Input>
        </Col>
      </Row>
      <Row>
        <Col xs="12">
          <Input
            hidden
            id="personLatLon"
            name="personLatLon"
            type="text"
            defaultValue={lonlat}
            innerRef={register}
          />
          <MapContainer onMarkerChange={onMarkerChange} />
        </Col>
      </Row>
      <h4>Inventory</h4>
      <Inventory
        onSubmitInventory={onSubmitInventory}
        currentInventory={inventory}
      />
      <Button
        outline
        color="secondary"
        className="mt-2"
        type="submit"
        form="registerForm"
      >
        Submit
      </Button>
    </Form>
  );
}

export default Register;

编辑:使用答案提示更新代码,仍然面临同样的问题:(

【问题讨论】:

  • 到底是什么问题?是不是onSubmitInventory里面的console.log(inventory)显示的是之前的状态?这是意料之中的,因为设置状态是异步的。如果您将该行移出该功能或使用开发工具,我认为您会看到库存正在按预期更新。

标签: reactjs components react-hooks reactstrap react-hook-form


【解决方案1】:

我在您的代码中看到的唯一问题是您试图将第二个参数传递给setInventory。我不相信这与类组件一样适用于钩子。当我尝试在打字稿中输入它时,它会立即引发错误。

只需传递您尝试发送的实际数据,然后调用onSubmitInventory(inventory),例如:

  const onChangeWeapon = (e) => {
    const newInventory = {...inventory, weapon: e.target.value};
    setInventory(newInventory);
    onSubmitInventory(newInventory);
  };

如果你必须等待下一个渲染调用onSubmitInventory,它应该是父级中的useEffect。我的下一个问题是为什么父母需要状态而孩子也有状态?可能它应该被抬起。但是如果没有看到父母,我不确定。


编辑:您的问题只是与console.log 有关吗?如果你跑

const [state, setState] = useState(true);
// ...
setState(false);
console.log(state); // outputs true.

state 的值在下一次渲染之前不会改变。调用setState 将导致新的渲染,但在此之前,旧值仍然存在。在父类中添加console.log 的状态,就像这里一样

const [inventory, setInventory] = useState({
    water: 0,
    soup: 0,
    meds: 0,
    weapon: 0,
  });
console.log('I just rendered, inventory: ', inventory);

你会看到它更新!

在这种情况下,state 的值不会改变这一事实可能会咬到你:

const [state, setState] = useState(true); // closes over this state!
// ...
setState(!state); // closes over the state value above
setState(!state); // closes over the same state value above
// state will be false on next render

所以如果有可能在渲染之前调用 setState 两次,我会使用此表单:

const [state, setState] = useState(true);
// ...
setState(state => !state);
setState(state => !state);
// state will be true on next render

【讨论】:

  • 我用我的新代码更新了我的问题,检查一下
  • 我添加了一些数据。
  • 是的!这是我的 console.log 哈哈 xD 我猜它一直在更新,我一直在为它吓坏了哈哈
  • 但是我很高兴我从你的回答中学到了一些新东西,谢谢!!!
  • 没问题,很高兴它有帮助。
【解决方案2】:

在父组件的onSubmitInventory 中,您已将参数命名为inventory,但该参数已存在于父组件的范围内。我建议重命名 newInventory 以明确您所引用的内容。

此外,您似乎正在跟踪父母和孩子状态的库存。将其保存在父级中,并将其作为 prop/props 传递给子级。

【讨论】:

  • 重命名参数,谢谢提示。我尝试将它传递给 rn,它实际上得到了 onChange 更多延迟:(
  • 如果您使用最新的代码更新您的问题,我会再看一遍。另外,不要忘记使用 useState 钩子设置状态是异步的。
  • 我用新代码更新了我的问题,检查一下,这次我复制粘贴了整个父组件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-19
  • 1970-01-01
相关资源
最近更新 更多