Redux 状态不会实时更新

huangapple go评论60阅读模式
英文:

Redux state is not updating real time

问题

以下是您要翻译的部分:

"I am making a signup form and I want to navigate a user to the login page when the signup is successful. On the signup page, I want to get the updated state. In the signup handler, I am not getting updated state (status) in real-time although the status is updating outside the function.

Signup Component

const Signup = () => {
  const [name, setName] = useState('')
  const [email, setEmail] = useState('')
  const [phone, setPhone] = useState('')
  const [password, setPassword] = useState('')

  const navigate = useNavigate();
  const token = localStorage.getItem("token");
  useEffect(() => {
    if (token) {
      navigate('/home')
    }
  }, [])

  const signupForm = [
    {
      label: "Name",
      InputIcon: FaUserAlt,
      width: '100%',
      handleInput: (e) => {
        setName(e.target.value)
      }
    },
    {
      label: "Email",
      InputIcon: FaEnvelope,
      width: '100%',
      handleInput: (e) => {
        setEmail(e.target.value)
      }
    },
    {
      label: "Phone",
      InputIcon: FaPhoneAlt,
      width: '100%',
      handleInput: (e) => {
        setPhone(e.target.value)
      }
    },
  ]

  const dispatch = useDispatch()

  const { status } = useSelector((state) => {
    return state.auth;
  })

  console.log(status);

  const signup = async (e) => {
    e.preventDefault()

    const user = {
      name,
      email,
      phone,
      password
    }

    await dispatch(signupUser(user))
    console.log(status);
    // if (status == 'success') {
    //   navigate('/')
    // }
  }

  return (
    <div className='form-container'>
      <img src={userImage} alt='user' />
      <form>
        <h2>Sign up</h2>
        {
          signupForm.map((data, i) => {
            return <OutlinedTextField
              key={i}
              style={{ width: data.width }}
              label={data.label}
              InputIcon={data.InputIcon}
              onChangeFunc={data.handleInput}
            />;
          })
        }

        <OutlinedPasswordTextField
          style={{ width: '100%' }}
          label={'Password'}
          InputIcon={FaLock}
          onChangeFunc={(e) => setPassword(e.target.value)}
        />

        <div className='btn-container'>
          <AppButton text={status == 'loading' ? <CircularProgress color='inherit' size="15px" /> : 'Sign up'}
            disabled={status == 'loading' ? true : false}
            onClickFunc={signup}
            style={{ width: '80px' }}
          />

          <Link to="/">Login</Link>
        </div>
      </form>
    </div>
  )
}

Authentication Slice

const STATUSES = {
  SUCCESS: 'success',
  LOADING: 'loading',
  FAIL: 'fail'
}

export const signupUser = createAsyncThunk('user/signup', async (user) => {
  const data = await axios.post('http://localhost:5000/api/signup', user)
    .then(res => {
      console.log(res.data);
    })
    .catch(err => {
      toast.error(err.response.data.message)
      throw an Error(err.response.data.message)
    })
  console.log(data);
  return data
})

const productsSlice = createSlice({
  name: 'product',
  initialState: {
    data: [],
    status: STATUSES,
    loading: false,
  },
  extraReducers: (builder) => {
    builder.addCase(signupUser.pending, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.LOADING
    })
    builder.addCase(signupUser.fulfilled, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.SUCCESS
      state.data = action.payload
    })
    builder.addCase(signupUser.rejected, (state, action) => {
      console.log(action, state)
      state.status = STATUSES.FAIL
    })
  }
})
英文:

I am making a signup form and I want to navigate a user to the login page when the signup is successful. On the signup page, I want to get the updated state. In the signup handler, I am not getting updated state (status) in real-time although the status is updating outside the function.

Signup Component

const Signup = () =&gt; {
  const [name, setName] = useState(&#39;&#39;)
  const [email, setEmail] = useState(&#39;&#39;)
  const [phone, setPhone] = useState(&#39;&#39;)
  const [password, setPassword] = useState(&#39;&#39;)

  const navigate = useNavigate();
  const token = localStorage.getItem(&quot;token&quot;);
  useEffect(() =&gt; {
    if (token) {
      navigate(&#39;/home&#39;)
    }
  }, [])

  const signupForm = [
    {
      label: &quot;Name&quot;,
      InputIcon: FaUserAlt,
      width: &#39;100%&#39;,
      handleInput: (e) =&gt; {
        setName(e.target.value)
      }
    },
    {
      label: &quot;Email&quot;,
      InputIcon: FaEnvelope,
      width: &#39;100%&#39;,
      handleInput: (e) =&gt; {
        setEmail(e.target.value)
      }
    },
    {
      label: &quot;Phone&quot;,
      InputIcon: FaPhoneAlt,
      width: &#39;100%&#39;,
      handleInput: (e) =&gt; {
        setPhone(e.target.value)
      }
    },
  ]

  const dispatch = useDispatch()

  const { status } = useSelector((state) =&gt; {
    return state.auth;
  })

  console.log(status);

  const signup = async (e) =&gt; {
    e.preventDefault()

    const user = {
      name,
      email,
      phone,
      password
    }

    await dispatch(signupUser(user))
    console.log(status);
    // if (status == &#39;success&#39;) {
    //   navigate(&#39;/&#39;)
    // }
  }

  return (
    &lt;div className=&#39;form-container&#39;&gt;
      &lt;img src={userImage} alt=&#39;user&#39; /&gt;
      &lt;form&gt;
        &lt;h2&gt;Sign up&lt;/h2&gt;
        {
          signupForm.map((data, i) =&gt; {
            return &lt;OutlinedTextField
              key={i}
              style={{ width: data.width }}
              label={data.label}
              InputIcon={data.InputIcon}
              onChangeFunc={data.handleInput}
            /&gt;
          })
        }

        &lt;OutlinedPasswordTextField
          style={{ width: &#39;100%&#39; }}
          label={&#39;Password&#39;}
          InputIcon={FaLock}
          onChangeFunc={(e) =&gt; setPassword(e.target.value)}
        /&gt;

        &lt;div className=&#39;btn-container&#39;&gt;
          &lt;AppButton text={status == &#39;loading&#39; ? &lt;CircularProgress color=&#39;inherit&#39; size=&quot;15px&quot; /&gt; : &#39;Sign up&#39;}
            disabled={status == &#39;loading&#39; ? true : false}
            onClickFunc={signup}
            style={{ width: &#39;80px&#39; }}
          /&gt;

          &lt;Link to=&quot;/&quot;&gt;Login&lt;/Link&gt;
        &lt;/div&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  )
}

Authentication Slice

const STATUSES = {
  SUCCESS: &#39;success&#39;,
  LOADING: &#39;loading&#39;,
  FAIL: &#39;fail&#39;
}

export const signupUser = createAsyncThunk(&#39;user/signup&#39;, async (user) =&gt; {
  const data = await axios.post(&#39;http://localhost:5000/api/signup&#39;, user)
    .then(res =&gt; {
      console.log(res.data);
    })
    .catch(err =&gt; {
      toast.error(err.response.data.message)
      throw new Error(err.response.data.message)
    })
  console.log(data);
  return data
})

const productsSlice = createSlice({
  name: &#39;product&#39;,
  initialState: {
    data: [],
    status: STATUSES,
    loading: false,
  },
  extraReducers: (builder) =&gt; {
    builder.addCase(signupUser.pending, (state, action) =&gt; {
      console.log(action, state)
      state.status = STATUSES.LOADING
    })
    builder.addCase(signupUser.fulfilled, (state, action) =&gt; {
      console.log(action, state)
      state.status = STATUSES.SUCCESS
      state.data = action.payload
    })
    builder.addCase(signupUser.rejected, (state, action) =&gt; {
      console.log(action, state)
      state.status = STATUSES.FAIL
    })
  }
})

答案1

得分: 2

代码部分已被排除,以下是内容的中文翻译:

主要问题是所选的 status 状态在回调作用域中被封闭,并且在 signup 回调的生命周期内不会更新。换句话说,它是对 status 值的陈旧闭包。

我在 signupUser 操作中看到的第二个问题是没有正确返回获取的数据;在记录 res.data.then 部分没有返回任何内容。

您正在使用一个返回 Promise 的异步 Thunk 操作。您可以等待已分发的 signupUser 来解析。使用 .unwrap 函数来检查异步操作是否成功。请参阅分发后检查错误处理 Thunk 错误以获取详细信息。

export const signupUser = createAsyncThunk(
  "user/signup",
  async (user, { rejectWithValue }) => {
    try {
      const { data } = await axios.post('http://localhost:5000/api/signup', user);
      console.log(data);
      return data;
    } catch(err) {
      toast.error(err.response.data.message);
      rejectWithValue(err.response.data.message);
    }
  }
);
const signup = async (e) => {
  e.preventDefault();

  const user = {
    name,
    email,
    phone,
    password
  };

  try {
    await dispatch(signupUser(user)).unwrap();
    navigate("/");
  } catch(error) {
    // 处理/忽略任何失败的用户注册尝试
  }
};
英文:

The main issue is that the selected status state is closed over in callback scope and will not ever update during the life of the signup callback. In other words, it is a stale closure over the status value.

A secondary issue I see in the signupUser action is not returning the fetched data correctly; nothing is returned from the .then-able that logs res.data.

> const data = await axios.post('http://localhost:5000/api/signup', user)
> .then(res => {
> console.log(res.data);
> // nothing returned here, data above is undefined!
> })

You are using an asynchronous Thunk action which returns a Promise. You can await the dispatched signupUser to resolve. Use the .unwrap function to check if the asynchronous action was successful or not. See Checking Errors after Dispatching and Handling Thunk Errors for details.

export const signupUser = createAsyncThunk(
  &quot;user/signup&quot;,
  async (user, { rejectWithValue }) =&gt; {
    try {
      const { data } = await axios.post(&#39;http://localhost:5000/api/signup&#39;, user);
      console.log(data);
      return data;
    } catch(err) {
      toast.error(err.response.data.message);
      rejectWithValue(err.response.data.message);
    }
  }
);
const signup = async (e) =&gt; {
  e.preventDefault();

  const user = {
    name,
    email,
    phone,
    password
  };

  try {
    await dispatch(signupUser(user)).unwrap();
    navigate(&quot;/&quot;);
  } catch(error) {
    // handle/ignore any failed user signup attempts
  }
};

答案2

得分: 1

const Signup = () => {

// ...

const { status } = useSelector((state) => state.auth)

useEffect(() => {
if (status === 'success') {
navigate('/')
}
}, [status])

const signup = async (e) => {
e.preventDefault()

const user = {
name,
email,
phone,
password
}
await disptach(signupUser(user))

}

return (
// ...
)
}

英文:
const Signup = () =&gt; {
// ...
const { status } = useSelector((state) =&gt; state.auth)
useEffect(() =&gt; {
if (status === &#39;success&#39;) {
navigate(&#39;/&#39;)
}
}, [status])
const signup = async (e) =&gt; {
e.preventDefault()
const user = {
name,
email,
phone,
password
}
await disptach(signupUser(user))
}
return (
// ...
)
}

You should check the status inside the Signup with useSelector. If your status changes you can re-trigger your navigate inside the useEffect with the dependency array.

huangapple
  • 本文由 发表于 2023年3月7日 22:10:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663068.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定