componentWillReceiveProps, componentDidUpdate для React Hook - PullRequest
0 голосов
/ 23 февраля 2019

Я сталкиваюсь с двумя проблемами:

  • Даже если, согласно руководству React, производное состояние не рекомендуется, но некоторые крайние случаи все еще нуждаются в этом.
    В терминахфункционального компонента с React Hook, Какова эквивалентная реализация с React Hook, если мне нужно производное состояние?который в компоненте класса будет обновляться в componentWillReceiveProps для каждого родительского рендера

, см. ниже пример кода:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: props.count > 100 ? 100 : props.count,
    }

  }

  /*What is the equivalent implementation when React Hook is used here componentWillReceiveProps*/
  componentWillReceiveProps(nextProps) {
    if (nextProps.count !== this.props.count) {
      this.setState({
        count: nextProps.count > 100 ? 100 : nextProps.count
      });
    }
  }

  render() {
    return ( <
      div > {
        this.state.count
      } <
      /div>
    );
  }
}

export default App;
  • Что касается componentDidUpdate, у componentDidUpdate есть свой couterpart, когда используется React Hook, это

    React.useEffect (()=> {return () => {

      };
    }, [parentProp]);
    

    Второй параметр для useEffect обеспечивает выполнение кода только при изменении реквизита, но что делать, если я хочу выполнить соответствующие задачи на основе нескольких соответствующих реквизитовразница ? как это сделать с помощью useEffect ?

см. пример кода:

class App extends Component {


  /*What is the equivalent implementation when functional component with React Hook is used here */
  componentDidUpdate(prevProps, prevState) {
    if (prevProps.groupName !== this.props.groupName) {
      console.log('Let'
        's say, I do want to do some task here only when groupName differs');
    } else if (prevProps.companyName !== this.props.companyName) {
      console.log('Let'
        's say,I do want to do some different task here only when companyName differs');
    }

  }


  render() {
    /*for simplicity, render code is ignored*/
    return null;
  }
}

export default App;

Ответы [ 3 ]

0 голосов
/ 23 февраля 2019

Я понимаю, что ваш пример "производного состояния" преднамеренно прост, но поскольку существует очень мало законных случаев производного состояния, трудно дать рекомендацию о замене, кроме как в каждом конкретном случае, поскольку он зависит отпричина, по которой вы используете производное состояние.В приведенном вами конкретном примере не было никакой причины использовать производное состояние в случае класса, и поэтому в случае ловушки по-прежнему нет никаких причин (значение может быть просто получено локально, не переводя его в состояние).Если производное значение дорого, вы можете использовать useMemo в качестве подарка Tholle.Если они не соответствуют более реалистичным случаям, которые вы имеете в виду, вам необходимо представить более конкретный случай, который действительно требует производного состояния.

Что касается вашего componentDidUpdate примера, если чтоВы хотите сделать для разных реквизитов независимо, тогда вы можете использовать отдельные эффекты для каждого (например, несколько useEffect вызовов).Если вы хотите сделать в точности , как в вашем примере (т.е. сделать что-то только для companyName изменения, если groupName также не изменилось, как указано вашим else if), тогда вы можете использовать ссылки для более сложных условий.Вы должны не изменять ссылку во время рендеринга (всегда существует возможность сброса / повторного выполнения рендера, если поддерживается параллельный режим), поэтому в примере используется последний эффект для обновления ссылок.В моем примере я использую ссылку, чтобы не выполнять эффектную работу на начальном рендере (см. Ответ Толле в на этот связанный вопрос ) и чтобы определить, изменился ли groupName при принятии решения, выполнять или нет работуосновано на companyName изменении.

const { useState, useEffect, useRef } = React;

const DerivedStateFromProps = ({ count }) => {
  const derivedCount = count > 100 ? 100 : count;

  return (
    <div>
      Derived from {count}: {derivedCount}{" "}
    </div>
  );
};
const ComponentDidUpdate = ({ groupName, companyName }) => {
  const initialRender = useRef(true);
  const lastGroupName = useRef(groupName);
  useEffect(
    () => {
      if (!initialRender.current) {
        console.log("Do something when groupName changes", groupName);
      }
    },
    [groupName]
  );
  useEffect(
    () => {
      if (!initialRender.current) {
        console.log("Do something when companyName changes", companyName);
      }
    },
    [companyName]
  );
  useEffect(
    () => {
      if (!initialRender.current && groupName === lastGroupName.current)
        console.log(
          "Do something when companyName changes only if groupName didn't also change",
          companyName
        );
    },
    [companyName]
  );
  useEffect(
    () => {
      // This effect is last so that these refs can be read accurately in all the other effects.
      initialRender.current = false;
      lastGroupName.current = groupName;
    },
    [groupName]
  );

  return null;
};
function App() {
  const [count, setCount] = useState(98);
  const [groupName, setGroupName] = useState("initial groupName");
  const [companyName, setCompanyName] = useState("initial companyName");
  return (
    <div>
      <div>
        <DerivedStateFromProps count={count} />
        <button onClick={() => setCount(prevCount => prevCount + 1)}>
          Increment Count
        </button>
      </div>
      <div>
        <ComponentDidUpdate groupName={groupName} companyName={companyName} />
        groupName:{" "}
        <input
          type="text"
          value={groupName}
          onChange={event => setGroupName(event.target.value)}
        />
        <br />
        companyName:{" "}
        <input
          type="text"
          value={companyName}
          onChange={event => setCompanyName(event.target.value)}
        />
        <br />
        change both{" "}
        <input
          type="text"
          onChange={event => {
            const suffix = event.target.value;
            setGroupName(prev => prev + suffix);
            setCompanyName(prev => prev + suffix);
          }}
        />
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<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>

Edit Derived state and componentDidUpdate

0 голосов
/ 29 апреля 2019

В вашем сценарии вам вообще не нужно использовать или повторно внедрять getDerivedStateFromProps.Вам просто нужно создать новую переменную, чтобы получить новую форму данных.Использование состояния в этом сценарии вызовет еще один повторный рендеринг, который не очень хорош для производительности.

import React from 'react';

const App = ({ count }) => {
  const derivedCount = count > 100 ? 100 : count;

  return (
    <div>Counter: {derivedCount}</div>
  );
}

App.propTypes = {
  count: PropTypes.number.isRequired
}

Демонстрация здесь: https://codesandbox.io/embed/qzn8y9y24j?fontsize=14

Вы можете прочитать больше о различных способах решения этих проблем.виды сценариев без использования getDerivedStateFromProps здесь: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Если вам действительно нужно использовать отдельное состояние, вы можете использовать что-то вроде этого

import React, { useState } from 'react';

const App = ({ count }) => {
  const [derivedCounter, setDerivedCounter] = useState(
    count > 100 ? 100 : count
  );

  useEffect(() => {
    setDerivedCounter(count > 100 ? 100 : count);
  }, [count]); // this line will tell react only trigger if count was changed

  return <div>Counter: {derivedCounter}</div>;
};
0 голосов
/ 23 февраля 2019

Вы можете использовать хук useMemo, чтобы сохранить вычисление и поместить props.count в массив, заданный в качестве второго аргумента, чтобы пересчитать значение при его изменении.

const { useState, useEffect, useMemo } = React;

function App() {
  const [count, setCount] = useState(50);

  useEffect(() => {
    setTimeout(() => {
      setCount(150);
    }, 2000);
  }, []);

  return <DisplayCount count={count} />;
}

function DisplayCount(props) {
  const count = useMemo(() => props.count > 100 ? 100 : props.count, [props.count]);

  return <div> {count} </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Самый простой способ сделать отдельные эффекты при смене отдельных реквизитов - это создать несколько useEffect ловушек, которые запускаются только при смене одного из отдельных реквизитов.

const { useState, useEffect } = React;

function App() {
  const [groupName, setGroupName] = useState('foo');
  const [companyName, setCompanyName] = useState('foo');

  useEffect(() => {
    setTimeout(() => {
      setGroupName('bar');
    }, 1000);
    setTimeout(() => {
      setCompanyName('bar');
    }, 2000);
  }, []);

  return <DisplayGroupCompany groupName={groupName} companyName={companyName} />;
}

function DisplayGroupCompany(props) {
  useEffect(() => {
    console.log("Let's say, I do want to do some task here only when groupName differs");
  }, [props.groupName])
  useEffect(() => {
    console.log("Let's say,I do want to do some different task here only when companyName differs");
  }, [props.companyName])

  return <div> {props.groupName} - {props.companyName} </div>;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>
...