useState的异常行为在初始渲染时反应了钩子

时间:2020-08-08 04:18:59

标签: javascript reactjs

我正在构建一个简单的应用程序,该应用程序可通过搜索到的输入值使用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会将svg元素附加到div标签名称 weatherSearch

问题是组件在渲染svg标签两次之前渲染了两次。但是当我注释掉两个状态初始化时,

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

该组件仅渲染一次,并绘制一次svg标签。

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

并且搜索组件是index.js的根组件,因此父组件无法触发重新呈现。

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

2 个答案:

答案 0 :(得分:1)

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

enter image description here

我会将这个逻辑放在带有空依赖性数组的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>
  )
}

答案 1 :(得分:0)

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

此外,将受控虚拟DOM与不受控制的D3混合也不是一个好主意。请看看这个钩子https://en.reactjs.org/docs/hooks-reference.html#uselayouteffect 以及该API https://en.reactjs.org/docs/portals.html,以了解如何实现这种情况。

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

其他提示:

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

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

<input onChange={onChangeText} />   

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