如何在初始渲染后重新渲染自定义挂钩

How to re-render a custom hook after initial render

我有一个名为 useIsUserSubscribed 的自定义挂钩,用于检查特定用户是否已订阅。 returns 如果用户已订阅则为真,如果用户未订阅则为假...

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 挂钩,以便我的按钮文本切换。这能做到吗?

添加对 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();
}

您不能为此目的在挂钩中使用 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>
  );
} 

您还可以在挂钩中添加一些加载状态,return 它们也可以检查进程是否已经完成

这是用户@bitspook

的另一个解决方案

SubscribeUnsubscribeBtn 依赖于 useIsUserSubscribed,但 useIsUserSubscribed 不依赖于 SubscribeUnsubscribeBtn 的任何内容。 相反,useIsUserSubscribed 保持本地状态。您在这里有几个选择:

  1. 将有关用户是否订阅的状态向上移动一个级别,因为您正在使用 Redux,也许在 Redux 中。
  2. useIsUserSubscribed沟通您需要更改其内部状态。

对于1)

  const [userIsSubscribed, setUserIsSubscribed] = useState(null);

将此状态移动到 Redux 存储并与 useSelector 一起使用。

对于 2),return 值数组和钩子回调,而不仅仅是值。它将允许您从组件通信回到钩子。

useIsUserSubscribed,

  return [userIsSubscribed, setUserIsSubscribed];

然后在onClick,你可以调用setUserIsSubscribed(false),改变钩子的内部状态,并重新渲染你的组件。