繁体   English   中英

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

[英]How to re-render a custom hook after initial render

我有一个名为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 返回的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>
  );
} 

您还可以在您的钩子中添加一些加载 state 并将它们也返回,这样您就可以检查过程是否已经完成

这是用户@bitspook 的另一个解决方案

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

  1. 由于您使用的是 Redux,可能在 Redux 中,移动 state 关于用户是否订阅的问题。
  2. 沟通使用useIsUserSubscribed需要更改其内部state。

对于1)

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

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

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

useIsUserSubscribed中,

  return [userIsSubscribed, setUserIsSubscribed];

然后在onClick中,您可以调用setUserIsSubscribed(false) ,更改钩子的内部 state 并重新渲染您的组件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM