React useState 布尔值不更新

问题描述 投票:0回答:1

我正在尝试创建一个自定义 toast 来显示错误通知。 Toast 的可见性取决于道具的

isShowed

export type ToastProps = {
  color: 'error' | 'success' | 'warning';
  message: string;
  isShowed: boolean;
};

const Toast = (props: ToastProps) => {
  const { color, message, isShowed } = props;

  const [showed, setShowed] = useState(false);

  console.log(isShowed, showed);

  useEffect(() => {
    if (isShowed) {
      setShowed(true);
    } else {
      setShowed(false);
    }
  }, [isShowed]);

  return (
    <div
      className="toast-notification-wrapper"
      style={{
        position: 'fixed',
        top: '20px',
        left: '50%',
        transform: 'translateX(-50%)',
        zIndex: 9999,
        padding: '15px',
        borderRadius: '5px',
        transition: 'all 0.3s ease-out',
        opacity: showed ? 1 : 0,
        pointerEvents: showed ? 'auto' : 'none',
        display: 'flex',
        justifyContent: 'space-between',
        minWidth: '150px',
        backgroundColor:
          color === 'error'
            ? '#dc3545'
            : color === 'success'
            ? '#28a745'
            : '#ffc107',
      }}
    >
      <p style={{ margin: 0, color: 'white', fontSize: '15px', lineHeight: 1 }}>
        {message}
      </p>
      <span
        style={{
          cursor: 'pointer',
          display: 'flex',
          alignContent: 'center',
          justifyContent: 'center',
          marginLeft: '30px',
        }}
        onClick={() => {
          setShowed(!isShowed);
        }}
      >
        <FontAwesomeIcon
          icon={faClose}
          style={{ color: 'white', fontSize: '15px' }}
        />
      </span>
    </div>
  );
};

这是我的吐司组件

const Login = (props: { isCompleted: boolean; isLoggedIn: boolean }) => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [showPassword, setShowPassword] = useState(false);
  const navigate = useNavigate();
  const [toast, setToast] = useState({
    isShowed: false,
    color: 'error',
    message: 'failed',
  } as ToastProps);

  const { isCompleted, isLoggedIn } = props;

  if (isCompleted === false) {
    return null;
  }

  if (isLoggedIn) {
    navigate('/');
  }

  const togglePasswordVisibility = () => {
    setShowPassword(showPassword ? false : true);
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    try {
      await login({ username, password });

      window.location.href = '/';
    } catch (error: any) {
      setToast({
        isShowed: true,
        color: 'error',
        message:
          error.response.data?.message ||
          'Unexpected Error. Please try again later',
      });
    }
  };

  return (
    <div className="login-form">
      <Toast
        isShowed={toast.isShowed}
        color={toast.color}
        message={toast.message}
      ></Toast>
      <div className="login-form-container">
        <h1>Login</h1>
        <form onSubmit={handleSubmit}>
          <div className="form-row">
            <label>Username</label>
            <input
              type="text"
              placeholder="Enter your username"
              required={true}
              onChange={e => setUsername(e.target.value)}
            ></input>
          </div>

          <div className="form-row">
            <label>Password</label>
            <input
              type={showPassword ? 'text' : 'password'}
              placeholder="********"
              required={true}
              onChange={e => setPassword(e.target.value)}
            ></input>
            <span
              onClick={togglePasswordVisibility}
              style={{
                position: 'absolute',
                right: '20px',
                top: 'calc(50% + 12px)',
                fontSize: '13px',
                transform: 'translateY(-50%)',
                cursor: 'pointer',
              }}
            >
              <FontAwesomeIcon
                icon={showPassword ? faEyeSlash : faEye}
                style={{ color: 'black' }}
              />
            </span>
          </div>

          <div className="button-wrapper">
            <button value={'Login'}>Login</button>
          </div>
        </form>
      </div>
    </div>
  );
};
const Login = (props: { isCompleted: boolean; isLoggedIn: boolean }) => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [showPassword, setShowPassword] = useState(false);
  const navigate = useNavigate();
  const [toast, setToast] = useState({
    isShowed: false,
    color: 'error',
    message: 'failed',
  } as ToastProps);

  const { isCompleted, isLoggedIn } = props;

  if (isCompleted === false) {
    return null;
  }

  if (isLoggedIn) {
    navigate('/');
  }

  const togglePasswordVisibility = () => {
    setShowPassword(showPassword ? false : true);
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    try {
      await login({ username, password });

      window.location.href = '/';
    } catch (error: any) {
      setToast({
        isShowed: true,
        color: 'error',
        message:
          error.response.data?.message ||
          'Unexpected Error. Please try again later',
      });
    }
  };

  return (
    <div className="login-form">
      <Toast
        isShowed={toast.isShowed}
        color={toast.color}
        message={toast.message}
      ></Toast>
      <div className="login-form-container">
        <h1>Login</h1>
        <form onSubmit={handleSubmit}>
          <div className="form-row">
            <label>Username</label>
            <input
              type="text"
              placeholder="Enter your username"
              required={true}
              onChange={e => setUsername(e.target.value)}
            ></input>
          </div>

          <div className="form-row">
            <label>Password</label>
            <input
              type={showPassword ? 'text' : 'password'}
              placeholder="********"
              required={true}
              onChange={e => setPassword(e.target.value)}
            ></input>
            <span
              onClick={togglePasswordVisibility}
              style={{
                position: 'absolute',
                right: '20px',
                top: 'calc(50% + 12px)',
                fontSize: '13px',
                transform: 'translateY(-50%)',
                cursor: 'pointer',
              }}
            >
              <FontAwesomeIcon
                icon={showPassword ? faEyeSlash : faEye}
                style={{ color: 'black' }}
              />
            </span>
          </div>

          <div className="button-wrapper">
            <button value={'Login'}>Login</button>
          </div>
        </form>
      </div>
    </div>
  );
};

这是我的登录表单组件

当错误事件触发时,Toast 通知第一次起作用。 但是当错误再次发生时,toast仍然不可见。 当我使用 console.log 检查状态时, props 中的

isShowed
为 true,但
showed
为 false。 请帮我。预先感谢。

reactjs typescript react-hooks
1个回答
0
投票

从 Toast 组件中删除

useEffect
块和
showed
状态,使用以下 ToastProps

export type ToastProps = {
  color: 'error' | 'success' | 'warning';
  message: string;
  handleCloseToast: ()=>void
};

使用handleCloseToast函数来关闭toast

并根据父级中的 Toast 状态,使用以下 Toast 道具

<Toast
        handleCloseToast={() => setToast(prev => ({...prev,isShowed:false})}
        color={toast.color}
        message={toast. Message}
 ></Toast>

这只是 @IdrisSelimi 所解释的代码形式

© www.soinside.com 2019 - 2024. All rights reserved.