繁体   English   中英

初始渲染时 useState 反应钩子的意外行为

[英]Unexpected behavior of useState react hook at initial render

我正在构建一个简单的应用程序,该应用程序通过搜索的输入值使用 d3 图显示数据。

export const Search = () => {
  const [country, setCountry] = React.useState('');
  const [weatherData, setWeatherData] = React.useState([]);

  const changeText = e => {
    setCountry(e.target.value)
  }

  const searchCountry = () => {
    fetch(`https://apiweatherhistory/dayone/country/${country}`).then(res => res.json().then(data => {
      setWeatherData(weatherData)
    }))
  }

  const svg = d3.select('.weatherSearch')
    .append('svg')
    .attr('width', 960)
    .attr('height', 500)
    .style('background', 'black')

  return (
    <div>
      <input type='text' onChange={(e) => changeText(e)} value={country} />
      <button onClick={searchCountry}>Search</button>
      <div className='weatherSearch' />
    </div>
  )
}

安装组件后,d3 在 div 标签名称weatherSearch上附加 svg 元素。

问题是组件渲染两次,然后 svg 标记也被绘制两次。 但是当我注释掉两个状态初始化时,

  const [country, setCountry] = React.useState('');
  const [weatherData, setWeatherData] = React.useState([]);

该组件仅呈现一次并绘制一次 svg 标记。

输入或点击交互尚未发生,这意味着甚至没有使用两个 state 更改方法(setCountry、setWeatherData)。

并且 Search 组件是 index.js 的根组件,因此父组件无法触发重新渲染。

为什么会这样? 只有初始化状态才能触发重新渲染?

我怀疑这是因为附加 svg 位于功能组件主体中,因此每次“渲染”组件时都会执行它。 我所说的“渲染”是指,当 React 框架在“渲染”阶段渲染 virtualDOM 树以计算差异时。 这与计算 DOM 刷新到实际DOM 以及效果和其他生命周期函数运行时的“提交”阶段不同。 “渲染”阶段可以通过几乎任意次数的反应来暂停、中止和重新启动。

在此处输入图像描述

我会将这个逻辑放在一个带有空依赖数组的useEffect挂钩中,以便它在组件挂载上运行。

export const Search = () => {
  const [country, setCountry] = React.useState('');
  const [weatherData, setWeatherData] = React.useState([]);

  const changeText = e => {
    setCountry(e.target.value)
  }

  const searchCountry = () => {
    fetch(`https://apiweatherhistory/dayone/country/${country}`).then(res => res.json().then(data => {
      setWeatherData(weatherData)
    }))
  }

  useEffect(() => {
    const svg = d3.select('.weatherSearch')
      .append('svg')
      .attr('width', 960)
      .attr('height', 500)
      .style('background', 'black');
  }, []);

  return (
    <div>
      <input type='text' onChange={(e) => changeText(e)} value={country} />
      <button onClick={searchCountry}>Search</button>
      <div className='weatherSearch' />
    </div>
  )
}

您无法确定组件将被重新渲染多少次。 它不是确定性算法,而是启发式算法。

此外,将受控虚拟 DOM 与不受控制的 D3 混为一谈也不是一个好主意。 Please look at this hook https://en.reactjs.org/docs/hooks-reference.html#uselayouteffect and this API https://en.reactjs.org/docs/portals.html to understand how to implement such cases.

但恕我直言,最好让 SVG 部分由 React 控制而不使用 D3。 您可以直接在虚拟 DOM 中使用 SVG 标签。

附加提示:

处理程序可以由useCallback缓存。 例如:

const onChange = useCallback(
  ({ target: { value } }) => setCountry(value),
  [setCountry],
);

<input onChange={onChangeText} />   

此外,最好将处理程序命名为onSomeEvent

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM