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

时间:2020-10-04 18:28:33

标签: javascript reactjs react-hooks setstate use-state

我有一个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 };

useSnackbar.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 };

2 个答案:

答案 0 :(得分:1)

的确,useSnackbar钩对true的默认值为showSnackbar,并且确实在3秒后变为false,但在display App.js停留在true上,这意味着即使在timeout之后。

由于display从未设置为false,因此Snackbar从未卸载,因此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

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

答案 1 :(得分:1)

您不会在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>
  );
}

Edit default-value-in-usestate-not-getting-re-initialised