Реагировать: как воспроизвести пример обратного вызова setstate определенного класса компонентов с помощью функционального usestate / useEffect?

Я пытаюсь преобразовать простой компонент React с обратным вызовом this.setstate в функциональный компонент с useState / useEffect, но я не могу воспроизвести ту же функциональность с последним.

Я использую пример простой системы предупреждений / уведомлений для добавления и удаления предупреждений после тайм-аута. Песочница здесь:

https://codesandbox.io/s/class-and-function-callback-comparison-54tus?file=/src/index.js

соответствующий код для сравнения:

const NoticesWithFunctionCallback = () => {
  const [state, setState] = useState({
    alerts: [],
    show: false
  });

  const addNotice = (e) => {
    setState({
      alerts: [...state.alerts, ""]
    });
  };

  useEffect(() => {
    (async () => {
      await timeout(3000);
      console.log("timeout occurred");
      const newAlerts = tail([...state.alerts]);
      setState({
        alerts: newAlerts
      });
    })();
  }, [state.alerts]);

  return (
    <div className="App">
      <h3>
        Notices using function component with UseState and UseEffect callback
      </h3>
      <Generator addNotice={addNotice} />
      <Container>
        {state.alerts.map((item, index) => (
          <Alert>some alert here</Alert>
        ))}
      </Container>
    </div>
  );
};

class NoticesWithClassCallback extends React.Component {
  state = {
    alerts: [],
    show: false
  };

  addNotice = (e) => {
    this.setState(
      {
        alerts: [...this.state.alerts, ""]
      },
      async () => {
        await timeout(3000);
        console.log("timeout occurred");
        const newAlerts = tail([...this.state.alerts]);
        this.setState({
          alerts: newAlerts
        });
      }
    );
  };

  render() {
    return (
      <div className="App">
        <h3>Notices using class component and setState callback</h3>
        <Generator addNotice={this.addNotice} />
        <Container>
          {this.state.alerts.map((item, index) => (
            <Alert>some alert here</Alert>
          ))}
        </Container>
      </div>
    );
  }
}

Я надеюсь получить предложения о том, как заменить правильно работающий компонент обратного вызова setstate компонента класса на функциональный компонент с использованием usestate / useeffect.

Любые предложения приветствуются.


person Laurence Fass    schedule 19.02.2021    source источник


Ответы (2)


Здесь нет необходимости в использовании эффекта. useEffect срабатывает каждый раз при изменении массива предупреждений: путем добавления в него элементов или удаления. Он также срабатывает сразу после инициализации, поэтому все запутывается. Вместо этого вам следует изменить функцию addNotice и обновить состояние, используя предыдущую, например:

const NoticesWithFunctionCallback = () => {
  const [state, setState] = useState({
    alerts: [],
    show: false
  });

  const addNotice = (e) => {
    setState({
      ...state,
      alerts: [...state.alerts, '']
    });
    (async () => {
      await timeout(3000);
      setState(prevState => {
        return {
          ...prevState,
          alerts: [...tail([...prevState.alerts])]
        }
      });
    })()
  };

  return (
    <div className="App">
      <h3>
        Notices using function component with UseState and UseEffect callback
      </h3>
      <p>I want this to replicate the class component but its broken...</p>
      <Generator addNotice={addNotice} />
      <Container>
        {state.alerts.map((item, index) => (
          <Alert key={index}>Alert</Alert>
        ))}
      </Container>
    </div>
  );
};
person Konstantin Samarin    schedule 20.02.2021
comment
решение! спасибо Константин. - person Laurence Fass; 20.02.2021

Чтобы перейти от класса к функциональному компоненту, нужно выполнить несколько шагов:

Шаг 1:

class NameOfComponent extends Component

становится

function NameOfComponent(props){

Шаг 2: удалите constructor

Шаг 3: удалите метод render(), оставьте return

Шаг 4. добавьте const перед всеми методами.

Шаг 5: удалите this.state по всему компоненту

Шаг 6. удалите все ссылки на «this» в компоненте.

Шаг 7: установите начальное состояние с помощью useState() (и импортируйте его из реакции)

Пример числа:

const [count, setCount] = useState(0) // the useState() param is the initial value

Пример для объекта:

const [form, setValues] = useState({
 id: 0,
 first: ‘’,
 last: ‘’,
 password: ‘’,
 subscribe: false
})

Шаг 8: - измените this.setState() заданным вами установщиком (например, setValues ​​или setCount на шаге 7)

this.setState({ count: this.state.count + 1 )} станет setCount(count+1)

Шаг 9: замените componentDidMount на useEffect

useEffect(() => {
 fetch(‘url’)
 .then(res => res.json())
 .then(items => setSomething(items)
 .catch(console.log(err))
}, [])

Шаг 10: замените componentDidUpdate или componentWillReceiveProps на useEffect.

useEffect(() => {
     console.log(myPropsToCheck+ " has changed ! ")
    }, [myPropsToCheck])
})
person Topsy    schedule 19.02.2021
comment
спасибо, но вопрос не относится к тому, как преобразовать компонент класса в функциональный компонент, поскольку это уже было сделано, а конкретно к тому, как воспроизвести функциональность обратного вызова setstate для этого конкретного примера. - person Laurence Fass; 19.02.2021