skip to Main Content

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.

2

Answers


  1. You are accessing the token only in your ‘onSubmitHandle()’. Only there you check for the token and not when the component rerender.

    Add ‘useEffect’ and check there for the token, and make the navigation in the useEffect.

    The Auth component rerenders, but the navigation change doesn’t executes.

    Login or Signup to reply.
  2. 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
        }
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search