如果 HTML 元素在函数内部更新,则 React SetState 在第二次渲染之前无法工作

React SetState not working till second render if HTML element updated inside a function

我正在处理一个 useState 示例,我发现它在第二次渲染之前不起作用。第一次它保持默认值 0。

import React, { useState } from "react";

const MyState = () => {
  const [age, setage] = useState(0);

  function handleClick() {
    var enteredAge = document.getElementById("txtAge").value;
    if (enteredAge) {
      setage(enteredAge);
      if (enteredAge > 50) {
        document.getElementById("lblResult").innerText = "Wow " + age + "! You're old, rest up!";
      } else {
        document.getElementById("lblResult").innerText = "Wow " + age + "! Young gun!";
      }
    }
  }

  return (
    <div>
      Enter Your Age:
      <input type="text" id="txtAge" name="age" />
      <button onClick={handleClick}> Check </button>
      <label id="lblResult"></label>
    </div>
  );
};

export default MyState;

如果我执行类似以下操作,这在每个渲染器上都可以正常工作。但是如果我像上面显示的那样在函数内部使用 setState,那么 innerText 中的年龄仍然保持旧值。为什么会这样,正确的做法是什么?

<label id="lblResult">{age}</label>

你不应该直接改变 DOM 节点,因为反应维护 V-DOM 并且你对 lblresult 所做的改变将在 re-render 之后丢失。正确的方法是使用状态值并有条件地渲染数据。

import React, { useState } from 'react';

const MyState = () => {
  const [age, setage] = useState(0);

  function handleClick() {
    var enteredAge = document.getElementById('txtAge').value;
    if (enteredAge) {
      setage(enteredAge);
    }
  }

  return (
    <div>
      Enter Your Age:
      <input type="text" id="txtAge" name="age" />
      <button onClick={handleClick}> Check </button>
      <label id="lblResult">
        {age > 50
          ? `"Wow ${age}! You're old, rest up!`
          : `Wow ${age}! Young gun!`}
      </label>
    </div>
  );
};

export default MyState;

更新:

正如 Bjorn 提到的,您可以使用 refs 而不是 getElementById 来获取输入字段值。 Ref 是一个在 re-render 之间具有持久引用的对象,ref 的 current 属性 持有 DOM 节点。

import React, { useState, useRef } from 'react';

const MyState = () => {
  const [age, setage] = useState(0);
  const inputRef = useRef(null);

  function handleClick() {
    var enteredAge = inputRef.current.value;
    if (enteredAge) {
      setage(enteredAge);
    }
  }

  return (
    <div>
      Enter Your Age:
      <input type="text" id="txtAge" name="age" ref={inputRef} />
      <button onClick={handleClick}> Check </button>
      <label id="lblResult">
        {age > 50
          ? `"Wow ${age}! You're old, rest up!`
          : `Wow ${age}! Young gun!`}
      </label>
    </div>
  );
};

export default MyState;