Search code examples
reactjstypescriptredux-toolkit

Component don't update after updating store


I have an asynchronous Thunk:

export const loginThunk = createAsyncThunk(
  "user/login",
  async ({ login, password }: { login: string; password: string }) => {
    const { token } = await authService.login(login, password);
    return token;
  }
);

Using it in store:

extraReducers: (builder) => {
  builder
    .addCase(loginThunk.fulfilled, (state, action) => {
      state.token = action.payload;
      state.isLoading = false;
      state.error = false;
    })
    .addCase(loginThunk.pending, (state) => {
      state.isLoading = true;
      state.error = false;
    })
    .addCase(loginThunk.rejected, (state, action) => {
      state.isLoading = false;
      state.error = true;
    })

Target component:

const Auth = () => {
  const dispatch = useAppDispatch();
  const { token, error } = useAppSelector((state) => state.auth);
  const [login, setLogin] = useState("");
  const [password, setPassword] = useState("");
  const navigate = useNavigate();

  const onSubmitHandle = async (e: FormEvent) => {
    e.preventDefault();
    if (login !== "" && password !== "") {
      await dispatch(loginThunk({ login: login, password: password }));
      !error && token !== null ? navigate("/editor") : null;
    }
  };
    
  return (
    <div className="login">
      <form className="login__form" onSubmit={onSubmitHandle}>
        <h2 className="login__title">Editor</h2>
        <label className="login-form__label">
          Login
          <input
            type="text"
            name="login"
            onChange={(e) => setLogin(e.target.value)}
          />
        </label>
        <label className="login-form__label">
          Пароль
          <input
            type="text"
            name="password"
            onChange={(e) => setPassword(e.target.value)}
          />
        </label>
        <button type="submit" className="login__button">
          Enter
        </button>
        {error ? "Error, try do it later" : null}
      </form>
    </div>
  );
};

After click "Enter" I get token and put it to store. In Redux-Devtools I see that token is in store, but component doesn't update. Interesting, but component updating, if I change field error, but token no.


Solution

  • createAsyncThunks always resolve. If you are wanting to use the returned token value then you need to do two things.

    1. Await and unwrap the resolved result. See Handling Think Results for more details.
    2. Save the returned token value instead of referencing the stale closure over the selected token value from the outer scope.

    Example:

    const onSubmitHandle = async (e: FormEvent) => {
      e.preventDefault();
    
      if (login !== "" && password !== "") {
        try {
          const token = await dispatch(
            loginThunk({ login, password })
          ).unwrap(); // <-- unwrap Thunk result
    
          // If we get this far then no error yet
          if (token !== null) {
            navigate("/editor");
          }
        } catch(error) {
          // catch/handle any thrown errors or rejected Promises
        }
      }
    };