如何在初始渲染后重新渲染自定义钩子
Posted
技术标签:
【中文标题】如何在初始渲染后重新渲染自定义钩子【英文标题】:How to re-render a custom hook after initial render 【发布时间】:2022-01-02 11:44:57 【问题描述】:我有一个名为useIsUserSubscribed
的自定义钩子,用于检查是否订阅了特定用户。如果用户已订阅则返回 true,如果用户未订阅则返回 false...
import useState, useEffect from "react";
import useSelector from "react-redux";
import checkSubscription from "../services";
// this hook checks if the current user is subscribed to a particular user(publisherId)
function useIsUserSubscribed(publisherId)
const [userIsSubscribed, setUserIsSubscribed] = useState(null);
const currentUserId = useSelector((state) => state.auth.user?.id);
useEffect(() =>
if (!currentUserId || !publisherId) return;
async function fetchCheckSubscriptionData()
try
const res = await checkSubscription(publisherId);
setUserIsSubscribed(true);
catch (err)
setUserIsSubscribed(false);
fetchCheckSubscriptionData();
, [publisherId, currentUserId]);
return userIsSubscribed;
export default useIsUserSubscribed;
...我有一个使用此钩子的按钮,它根据从useIsUserSubscribed
...返回的布尔值有条件地呈现文本
import React, useEffect, useState from "react";
import add, remove from "../../services";
import useIsUserSubscribed from "../../hooks/useIsUserSubscribed";
const SubscribeUnsubscribeBtn = (profilePageUserId) =>
const userIsSubscribed = useIsUserSubscribed(profilePageUserId);
const onClick = async () =>
if (userIsSubscribed)
// this is an API Call to the backend
await removeSubscription(profilePageUserId);
else
// this is an API Call to the backend
await addSubscription(profilePageUserId);
// HOW CAN I RERENDER THE HOOK HERE!!!!?
return (
<button type="button" className="sub-edit-unsub-btn bsc-button" onClick=onClick>
userIsSubscribed ? 'Subscribed' : 'Unsubscribed'
</button>
);
在onClick
之后,我想重新渲染useIsUserSubscribed
挂钩,以便我的按钮文本切换。这个可以吗?
【问题讨论】:
【参考方案1】:你不能为此目的在你的钩子中使用 useEffect 试试这个:
钩子:
function useIsUserSubscribed()
const currentUserId = useSelector((state) => state.auth.user?.id);
const checkUser = useCallback(async (publisherId, setUserIsSubscribed) =>
if (!currentUserId || !publisherId) return;
try
const res = await checkSubscription(publisherId);
setUserIsSubscribed(true);
catch (err)
setUserIsSubscribed(false);
, [currentUserId]);
return checkUser;
export default useIsUserSubscribed;
组件:
const SubscribeUnsubscribeBtn = (profilePageUserId) =>
const [userIsSubscribed,setUserIsSubscribed]=useState(false);
const checkUser = useIsUserSubscribed();
useEffect(()=>
checkUser(profilePageUserId,setUserIsSubscribed)
,[checkUser,profilePageUserId]);
const onClick = async () =>
if (userIsSubscribed)
// this is an API Call to the backend
await removeSubscription(profilePageUserId);
else
// this is an API Call to the backend
await addSubscription(profilePageUserId);
// HOW CAN I RERENDER THE HOOK HERE!!!!?
checkUser(profilePageUserId,setUserIsSubscribed)
return (
<button type="button" className="sub-edit-unsub-btn bsc-button" onClick=onClick>
userIsSubscribed ? 'Subscribed' : 'Unsubscribed'
</button>
);
你也可以在你的钩子中添加一些加载状态并返回它们,这样你就可以检查过程是否已经完成
【讨论】:
我打算在应用程序的其他部分重用这个逻辑。如果钩子不是最好的方法,有什么更好的方法可以使其可重用? @SimoneAnthony hooks 没什么问题,你可以使用它,但我也注意到你使用 redux,所以你也可以使用 redux-thunk 动作【参考方案2】:添加对 useIsUserSubscribed 的 useEffect 的依赖。
钩子:
function useIsUserSubscribed(publisherId)
const [userIsSubscribed, setUserIsSubscribed] = useState(null);
const currentUserId = useSelector((state) => state.auth.user?.id);
// add refresh dependece
const refresh = useSelector((state) => state.auth.refresh);
useEffect(() =>
...
, [publisherId, currentUserId, refresh]);
...
组件:
const onClick = async () =>
...
// HOW CAN I RERENDER THE HOOK HERE!!!!?
// when click, you can dispatch a refresh flag.
dispatch(refreshSubState([]))
公开 forceUpdate 方法。
钩子:
function useIsUserSubscribed(publisherId)
const [update, setUpdate] = useState();
const forceUpdate = () =>
setUpdate();
return userIsSubscribed, forceUpdate;
组件:
const userIsSubscribed, forceUpdate = useIsUserSubscribed(profilePageUserId);
const onClick = async () =>
...
forceUpdate();
【讨论】:
【参考方案3】:这是用户@bitspook 的另一个解决方案
SubscribeUnsubscribeBtn
依赖于 useIsUserSubscribed
,但 useIsUserSubscribed
不依赖于来自 SubscribeUnsubscribeBtn
的任何内容。
相反,useIsUserSubscribed
保持本地状态。你有几个选择:
-
将有关用户是否订阅的状态上移一级,因为您正在使用 Redux,可能在 Redux 中。
通知
useIsUserSubscribed
,您需要更改其内部状态。
对于1)
const [userIsSubscribed, setUserIsSubscribed] = useState(null);
将此状态移动到 Redux 存储并与 useSelector 一起使用。
对于2)
,从钩子返回一个值数组和回调,而不仅仅是值。它将允许您从组件通信回钩子。
在useIsUserSubscribed
,
return [userIsSubscribed, setUserIsSubscribed];
然后在onClick
,你可以调用setUserIsSubscribed(false)
,改变钩子的内部状态,重新渲染你的组件。
【讨论】:
以上是关于如何在初始渲染后重新渲染自定义钩子的主要内容,如果未能解决你的问题,请参考以下文章
为啥带有 useContext 触发器的自定义路由 HOC 会重新渲染?