Могу ли я использовать useEffect в React для обновления отображения страницы? - PullRequest
1 голос
/ 12 мая 2019

Я пытаюсь использовать useEffect для обновления DOM при каждом обновлении определенного состояния или проп. Консоль записывает мои тесты, когда я хочу, чтобы обновление DOM сработало, но обновление или рендеринг DOM никогда не происходит

Я пытался найти способы сделать условный оператор, который бы проверял, изменилась ли подпора или состояние, в котором я хочу использовать useEffect, но поскольку функция запускает мои журналы консоли, а не мой рендер, там должно быть что-то, что я скучаю.

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 работает и правильный тип блока выводится на консоль из родительского компонента, текст на экране не изменяется в соответствии с ним.

1 Ответ

4 голосов
/ 12 мая 2019

Поскольку компонент перерисовывается всякий раз, когда изменяется реквизит (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, вы можете использовать useEffect с useState. useEffect обрабатывает побочные эффекты и не отображает компонент повторно, а изменение переменной будет потеряно при следующем отображении. 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.memo с обратным вызовом areEqual и проверять только на 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>
...