React onChange 未在受控 select 字段上触发

React onChange not fired on controlled select field

以下代码看似正确,但未按预期工作。

import React, {useEffect, useState} from "react"
import ReactDOM from "react-dom"

const Page = (props) => {
    const [selectedValue, setSelectedValue] = useState(0)

    const handleChange = () => {
        console.log(1)
    }

    useEffect(() => {
        setTimeout(() => {
            setSelectedValue(2)
        }, 1500)
    }, [])

    return (
        <div>
            <select onChange={handleChange} value={selectedValue}>
                <option value="1">1</option>
                <option value="2">2</option>
            </select>
        </div>
     )
}

ReactDOM.render(
    <Page />,
    document.getElementById('pageRoot')
)

因此,当页面在 1.5 秒后加载时,它会选择第二个选项,但不会触发 onChange,因为控制台中没有任何记录。

我猜是因为当元素失去焦点时会发生 onchange,因此在以编程方式更改此元素时,焦点永远不会发生。

以编程方式设置状态不能触发 onChange 处理程序,否则我们会发现自己处于以下组件的无限循环中:

const Page = (props) => {
  const [selectedValue, setSelectedValue] = useState(0)

  const handleChange = (e) => {
    setSelectedValue(e.target.value)
    console.log(1)
  }

  useEffect(() => {
    setTimeout(() => {
      setSelectedValue(2)
    }, 1500)
  }, [])

  return (
    <div>
      <select onChange={handleChange} value={selectedValue}>
        <option value="1">1</option>
        <option value="2">2</option>
      </select>
    </div>
  )
}

handleChange 触发 setSelectedValue,触发 handleChange,触发 setSelectedValue ...

所以是的,以编程方式设置状态不会触发 onChange 处理程序是预期的行为。

但是,您可以确保始终通过代理,具体取决于您想要实现的目标:

const Page = (props) => {
  const [selectedValue, setSelectedValue] = useState(0)

  const handleChange = (newValue) => {
    setSelectedValue(newValue)
    console.log(1)
  }

  useEffect(() => {
    setTimeout(() => {
      handleChange(2)
    }, 1500)
  }, [])

  return (
    <div>
      <select onChange={(e) => handleChange(e.target.value)} value={selectedValue}>
        <option value="1">1</option>
        <option value="2">2</option>
      </select>
    </div>
  )
}