useState 中的默认值未重新初始化

Default value in useState not getting re-initialised

我有一个 snack bar component,它显示祝酒消息几秒钟然后消失。我有一个 App component,其中包含一个 button,单击 button,我想控制 snack bar component。第一次单击时,snack bar 显示正常,并在指定时间结束后消失。但是当我再次点击它时, snack bar 没有出现。我每次都将 show state 初始化为 true,但 snack bar 仍然没有出现。请告诉我我哪里出错了以及如何纠正这个问题。以下是文件。我正在使用自定义挂钩来控制小吃店外观的行为。

App.js

import React, { useState } from "react";
import { Snackbar } from "./Snackbar";

function App() {
  const [display, setDisplay] = useState(false);
  return (
    <div>
      <button onClick={() => setDisplay(true)}>Click me</button>
      {display && <Snackbar message="hello" />}
    </div>
  );
}

export default App;

Snackbar.js

import React from "react";
import { useSnackbar } from "./useSnackbar";

const Snackbar = ({ message }) => {
  const { showSnackbar } = useSnackbar();
  return (
    showSnackbar && (
      <div>
        <p>{message}</p>
      </div>
    )
  );
};

export { Snackbar };

使用Snackbar.js

import { useState, useEffect } from 'react';

const useSnackbar = () => {
  const [showSnackbar, setSnackbar] = useState(true);
  const [snackbarMessage, setSnackbarMessage] = useState('');

  useEffect(() => {
    const timer = setTimeout(() => {
      setSnackbar(false);
      setSnackbarMessage('');
    }, 3000);

    return () => {
      clearTimeout(timer);
    };
  }, [showSnackbar]);

  return {
    showSnackbar,
    setSnackbar,
    snackbarMessage,
    setSnackbarMessage
  };
};

export { useSnackbar };

确实 useSnackbar 钩子对于 showSnackbar 的默认值为 true 并且它确实在 3 秒后更改为 false 但是 displayApp.js 中保持 true 这意味着即使在 timeout.

之后

由于 display 永远不会设置为 falseSnackbar 永远不会卸载,所以 useSnackbar 的状态永远不会重新初始化。

我的建议是更改 useSnackbar 的使用结构。

我会将 useSnackbar 移动到 App.js 并在那里设置小吃店打开状态。

function App() {
  const { showSnackbar, setSnackbar } = useSnackbar();

  return (
    <div>
      <button onClick={() => setSnackbar(true)}>Click me</button>
      {showSnackbar && <Snackbar message="hello" />}
    </div>
  );
}
const useSnackbar = () => {
  const [showSnackbar, setSnackbar] = useState(false);

  // rest of code
};
const Snackbar = ({ message }) => (
  <div>
    <p>{message}</p>
  </div>
);

Code Sandbox Demo

还有其他构造组件的方法,但对于您的示例,这将是最简单的解决方案之一。

您没有在 App.js

重置您的 display 状态

解决方案

将重置状态回调函数传递给 Snackbar 以传递给 useSnackbar 挂钩。

const useSnackbar = (onClose) => { // <-- callback function
  const [showSnackbar, setSnackbar] = useState(true);
  const [snackbarMessage, setSnackbarMessage] = useState('');

  useEffect(() => {
    const timer = setTimeout(() => {
      setSnackbar(false);
      setSnackbarMessage('');
      onClose && onClose(); // <-- invoke when timer expired
    }, 3000);

    return () => {
      clearTimeout(timer);
      onClose && onClose(); // <-- edge case if component unmounts before expire
    };
  }, [onClose, showSnackbar]);

  return {
    showSnackbar,
    setSnackbar,
    snackbarMessage,
    setSnackbarMessage
  };
};

const Snackbar = ({ message, onClose }) => {
  const { showSnackbar } = useSnackbar(onClose); // <-- pass callback to hook
  return (
    showSnackbar && (
      <div>
        <p>{message}</p>
      </div>
    )
  );
};

export default function App() {
  const [display, setDisplay] = useState(false);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <div>
      <button onClick={() => setDisplay(true)}>Click me</button>
      {display && <Snackbar message="hello" onClose={() => setDisplay(false)} />} // <-- pass reset callback
    </div>
    </div>
  );
}