Как получить доступ к состоянию при размонтировании компонента с помощью React Hooks? - PullRequest
2 голосов
/ 28 февраля 2020

С обычным React можно получить что-то вроде этого:

class NoteEditor extends React.PureComponent {

    constructor() {
        super();

        this.state = {
            noteId: 123,
        };
    }

    componentWillUnmount() {
        logger('This note has been closed: ' + this.state.noteId);
    }

    // ... more code to load and save note

}

В React Hooks можно написать так:

function NoteEditor {
    const [noteId, setNoteId] = useState(123);

    useEffect(() => {
        return () => {
            logger('This note has been closed: ' + noteId); // bug!!
        }
    }, [])

    return '...';
}

Будет выполнено то, что возвращается из useEffect только один раз перед размонтированием компонента, однако состояние (как в приведенном выше коде) устареет.

Решением будет передача noteId в качестве зависимости, но тогда эффект будет выполняться при каждом рендеринге, не один раз Или использовать ссылку, но это очень сложно поддерживать.

Итак, есть ли рекомендуемый шаблон для реализации этого с помощью React Hook?

С обычным React можно получить доступ к состоянию из где-нибудь в компоненте, но с крючками кажется, что есть только запутанные пути, каждый с серьезными недостатками, или, может быть, я просто что-то упускаю.

Любое предложение?

1 Ответ

3 голосов
/ 29 февраля 2020

useState() является специализированной формой useReducer(), поэтому вы можете заменить полный редуктор, чтобы получить текущее состояние и обойти проблему закрытия.

NoteEditor

import React, { useEffect, useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "set":
      return action.payload;
    case "unMount":
      console.log("This note has been closed: " + state); // This note has been closed: 201
      break;
    default:
      throw new Error();
  }
}

function NoteEditor({ initialNoteId }) {
  const [noteId, dispatch] = useReducer(reducer, initialNoteId);

  useEffect(function logBeforeUnMount() {
    return () => dispatch({ type: "unMount" });
  }, []);

  useEffect(function changeIdSideEffect() {
    setTimeout(() => {
      dispatch({ type: "set", payload: noteId + 1 });
    }, 1000);
  }, []);

  return <div>{noteId}</div>;
}
export default NoteEditor;

Приложение

import React, { useState, useEffect } from "react";
import "./styles.css";
import NoteEditor from "./note-editor";

export default function App() {
  const [notes, setNotes] = useState([100, 200, 300]);

  useEffect(function removeNote() {
    setTimeout(() => {
      setNotes([100, 300]);
    }, 2000);
  }, []);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      {notes.map(note => (
        <NoteEditor key={`Note${note}`} initialNoteId={note} />
      ))}
    </div>
  );
}
...