我可以在 React 中使用 useEffect 来更新页面显示吗?

Can I use useEffect in React to update page display?

我正在尝试使用 useEffect 在某个状态或道具更新时更新 DOM。当我希望 DOM 更新触发时,控制台会记录我的测试,但 DOM 更新或渲染从未发生

我试图找出方法来制作一个条件语句来检查我希望 useEffect 触发的 prop 或状态是否已更改,但由于函数是 运行 我的控制台日志,但不是我的渲染,一定是我遗漏了什么。

import React, { useEffect } from 'react';

function Report( { weatherData, units }) {

    let windUnits = 'mph';

    useEffect(() => {
        units == 'imperial' ? windUnits = 'mph' : windUnits = 'km/h';
        console.log('units updated');
    }, [ weatherData ]);

    return (
        <p id="wind">{ `${ weatherData.wind.speed }${ windUnits } ${ getWindDirection(weatherData.wind.deg) }`}</p>
    )
}

export default Report;

单位来自父组件,可以通过两个按钮更改以接收不同的测量数据。 weatherData 是从 api 接收到的数据,当 api 被调用并且数据发生变化时,windUnits 应该根据指定的单位类型发生变化。

目前,虽然 console.log 有效并且正确的单位类型从父组件打印到控制台,但屏幕上的文本不会更改以匹配它。

由于只要 props (weatherData, units) 发生变化,组件就会重新渲染,因此您不需要使用效果。只需将正确的单位分配给 windUnits:

const { useState } = React;

const getWindDirection = x => x;

const Report = ({ weatherData, units }) => {
  const windUnits = units == 'imperial' ? 'mph' : 'km/h';

  return (
    <p id="wind">{ `${ weatherData.wind.speed }${ windUnits } ${ getWindDirection(weatherData.wind.deg) }`}</p>
  );
};

const Demo = () => {
  const [units, setUnits] = useState('imperial');
  
  return (
    <div>
      <button onClick={() => setUnits('imperial')}>imperial</button>
      <button onClick={() => setUnits('metric')}>metric</button>
      
      <Report weatherData={{ wind: { speed: 10, deg: 45 }}} units={units} />
    </div>
  )
};

ReactDOM.render(
  <Demo />,
  demo
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

如果您只想在 windData 更改时重新渲染组件,您可以使用 useEffectuseStateuseEffect 处理副作用,不重新渲染组件,对变量的更改将在下一次渲染时丢失。 useState 另一方面将重新渲染组件。

const { useState, useEffect } = React;

const getWindDirection = x => x;

const Report = ({ weatherData, units }) => {
  const [windUnits, setWindUnits] = useState('mph');

  useEffect(() => {
    setWindUnits(units == 'imperial' ? 'mph' : 'km/h');
  }, [weatherData]);

  return (
    <p id="wind">{ `${ weatherData.wind.speed }${ windUnits } ${ getWindDirection(weatherData.wind.deg) }`}</p>
  );
};

const windData = { wind: { speed: 10, deg: 45 }};

const Demo = () => {
  const [units, setUnits] = useState('imperial');
  const [data, setData] = useState(windData);
  
  const changeSpeed = () => setData({ ...data, wind: { ...data.wind, speed: Math.random() * 100 }});
    
  return (
    <div>
      <button onClick={() => setUnits('imperial')}>imperial</button>
      <button onClick={() => setUnits('metric')}>metric</button>
      <button onClick={changeSpeed}>Change speed</button>
      
      <Report weatherData={data} units={units} />
    </div>
  )
};

ReactDOM.render(
  <Demo />,
  demo
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

另一种选择是将 React.memoareEqual 回调一起使用,并且只检查 weatherData 是否相等:

const { useState, memo } = React;

const getWindDirection = x => x;

const Report = memo(({ weatherData, units }) => {
  const windUnits = units == 'imperial' ? 'mph' : 'km/h';

  return (
    <p id="wind">{ `${ weatherData.wind.speed }${ windUnits } ${ getWindDirection(weatherData.wind.deg) }`}</p>
  );
}, ({ weatherData: prev }, { weatherData: current }) => prev === current);

const windData = { wind: { speed: 10, deg: 45 }};

const Demo = () => {
  const [units, setUnits] = useState('imperial');
  const [data, setData] = useState(windData);
  
  const changeSpeed = () => setData({ ...data, wind: { ...data.wind, speed: Math.random() * 100 }});
    
  return (
    <div>
      <button onClick={() => setUnits('imperial')}>imperial</button>
      <button onClick={() => setUnits('metric')}>metric</button>
      <button onClick={changeSpeed}>Change speed</button>
      
      <Report weatherData={data} units={units} />
    </div>
  )
};

ReactDOM.render(
  <Demo />,
  demo
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>