useState钩具有陈旧数据

时间:2019-10-11 20:29:24

标签: javascript reactjs

我正在使用useState钩子将下拉列表中的值设置为变量。在onDeviceIdChange内的特定实例中,我将setDeviceId设置为value,这是从我正在使用的组件库中的事件发出的。 value保持我期望的正确值,但是不会立即更新为deviceId

我已经阅读了与此问题相关的各种文章,最常见的解决方案似乎是将变量传递给useEffect的第二个参数

像这样

  useEffect(() => {
    fetchDeviceIds();
    console.log("call back for device id selection", deviceId);
  }, [deviceId]);

但这似乎只是为我反复触发fetchDeviceIds函数,而从未实际调用更改处理程序。我用useEffect尝试了几种变体,但无法在deviceId内获得更新后的值

下面是我的初始实现,没有将deviceId放入useEffect的第二个参数中

import React, { useState, useEffect } from "react";
import axios from "axios";

import { Dropdown, Form, Input } from "semantic-ui-react";

const Example = props => {
  const [deviceId, setDeviceId] = useState("");
  const [hardware, setHardware] = useState([]);
  const [error, setError] = useState("");
  const [versionNum, setVersionNum] = useState("");
  const [isLoading, setIsLoading] = useState(true);

  async function fetchDeviceIds() {
    const url = "/api/hardware";
    try {
      const response = await fetch(url, {
        method: "GET"
      });

      if (!response.ok) {  
        setError(response.statusText);
      }

      const data = await response.json();
      console.log(data);   
      setHardware(data.hardware);
    } catch (error) {
      console.log("error catch", error);
      console.log("Handle networking errors", error.message);
    }
  }

  const versionUrl = "/api/versionUrl";
  const onDeviceIdChange = async (e, { value }) => {
    console.log("device id value --> ", value);
    setDeviceId(value);
    console.log(deviceId); //this won't update immediately

    if (!deviceId) {
      handleClear();
      return;
    }

      try {
        console.log("calling versionUrl");
        const response = fetch(versionUrl, {
          method: "POST",
          body: JSON.stringify({
            deviceId: deviceId
          }),
          headers: {
            "Content-Type": "application/json"
          }
        });

        if (!response.ok) {
          setError(response.statusText);
        }
        const data = response.json();
        console.log(data);
        setVersionNum(data.version_num);
        setIsLoading(false);
      } catch (error) {
        console.log("error catch", error);
        console.log("Handle networking errors", error.message);
      }
    }
  };

  useEffect(() => {
    fetchDeviceIds();
  }, []);

  const handleClear = () => {
    setDeviceId("");
    setVersionNum("");
    setIsLoading(true);
  };

  return (
    <>
      <Form.Field required>
        <label style={{ display: "block" }}>device Id</label>
        <Dropdown
          id="deviceId"
          value={deviceId}
          onChange={onDeviceIdChange}
          selection
          fluid
          placeholder="Please select an device ID"
          clearable
          options={hardware.map(device => {
            return {
              text: device.id,
              value: device.id,
              key: device.id
            };
          })}
          style={{ marginTop: ".33", marginBottom: "2em" }}
        />
      </Form.Field>
      <Form.Field>
        <label style={{ display: "block", marginTop: "2em" }}>Version Number</label>

        {isLoading ? (
          <Input
            loading
            iconPosition="left"
            fluid
            placeholder="Choose an device ID..."
            value={versionNum}
          />
        ) : (
          <Input value={versionNum} fluid readOnly />
        )}
      </Form.Field>
    </>
  );
};

export default Example;

2 个答案:

答案 0 :(得分:0)

setDeviceId(value);
console.log(deviceId); //this won't update immediately because setDeviceId is asynchronous!
  

setState()不会立即使this.state突变,而是创建一个挂起的状态转换。调用此方法后访问this.state可能会返回现有值。无法保证对setState调用的同步操作,并且可能会批量调用以提高性能。

React不会更新状态,当您在console.log中获取先前的值

对于您而言,您可以将value用于所有同步操作,因为它是您要设置的新值,或者您可以将setState下面的所有代码移到useEffect

内部

更新

从下面的代码和docs

onChange={onDeviceIdChange}

第二个参数将是形状为{data: value}的对象 因此您的参数value将始终是不确定的!

const onDeviceIdChange = async (e, {value}) => {
    console.log("device id value --> ", value);
    console.log("device value from event --> ", e.target.value);
    setDeviceId(value);
  };

useEffect(() => {
    if (!deviceId) {
      handleClear();
      return;
    }
      try {
        console.log("calling versionUrl");
        const response = fetch(versionUrl, {
          method: "POST",
          body: JSON.stringify({
            deviceId: deviceId
          }),
          headers: {
            "Content-Type": "application/json"
          }
        });

        if (!response.ok) {
          setError(response.statusText);
        }
        const data = response.json();
        console.log(data);
        setVersionNum(data.version_num);
        setIsLoading(false);
      } catch (error) {
        console.log("error catch", error);
        console.log("Handle networking errors", error.message);
      }
    }
},[deviceId])

答案 1 :(得分:0)

SIGHUP应该看起来像这样:

onDeviceIdChange

,其余代码应进入依赖于const onDeviceIdChange = async (e, { value }) => { // code for validating value etc setDeviceId(value); } 的{​​{1}}钩子:

useEffect