【问题标题】: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 挂钩,以便我的按钮文本切换。这个可以吗?

【问题讨论】:

    标签: javascript reactjs react-hooks react-functional-component


    【解决方案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 保持本地状态。你有几个选择:

      1. 将有关用户是否订阅的状态上移一级,因为您正在使用 Redux,可能在 Redux 中。
      2. 通知useIsUserSubscribed,您需要更改其内部状态。

      对于1)

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

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

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

      useIsUserSubscribed

        return [userIsSubscribed, setUserIsSubscribed];
      

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

      【讨论】:

        猜你喜欢
        • 2022-01-02
        • 1970-01-01
        • 1970-01-01
        • 2022-01-10
        • 1970-01-01
        • 2022-07-18
        • 2020-06-27
        • 2021-11-02
        • 1970-01-01
        相关资源
        最近更新 更多