简体   繁体   English

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

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

I have custom hook named useIsUserSubscribed that checks to see a specific user is subscribed.我有一个名为useIsUserSubscribed的自定义钩子,它检查是否订阅了特定用户。 It returns true if the user is subscribed and false if the user is not subscribed...如果用户已订阅,则返回 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;

...I have a button using this hook that renders text conditionally based on the boolean returned from 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>
  );
} 

After onClick I would like to rerender my the useIsUserSubscribed hook So that my button text toggles.onClick之后,我想重新渲染我的useIsUserSubscribed钩子,以便我的按钮文本切换。 Can this be done?这可以做到吗?

Add a dependece on useIsUserSubscribed's useEffect.添加对useIsUserSubscribed 的useEffect 的依赖。

hook:钩:

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]);
    ...
}

component:零件:

const onClick = async () => {
    ...
    // HOW CAN I RERENDER THE HOOK HERE!!!!?
    // when click, you can dispatch a refresh flag.
    dispatch(refreshSubState([]))
}

Expose forceUpdate metheod.公开 forceUpdate 方法。

hook:钩:

function useIsUserSubscribed(publisherId) {
    const [update, setUpdate] = useState({});
    const forceUpdate = () => {
        setUpdate({});
    }  

    return {userIsSubscribed, forceUpdate};
}

component:零件:

const {userIsSubscribed, forceUpdate} = useIsUserSubscribed(profilePageUserId);

const onClick = async () => {
    ...
    forceUpdate();
}

you can not use useEffect in your hook for that purpose try this:你不能为此目的在你的钩子中使用 useEffect 试试这个:

hook:钩:

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;

component:零件:

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>
  );
} 

you can also add some loading state in your hook and return them too so you can check if process is already done or not您还可以在您的钩子中添加一些加载 state 并将它们也返回,这样您就可以检查过程是否已经完成

Here is another solution by user @bitspook这是用户@bitspook 的另一个解决方案

SubscribeUnsubscribeBtn has a dependency on useIsUserSubscribed , but useIsUserSubscribed don't depend on anything from SubscribeUnsubscribeBtn . SubscribeUnsubscribeBtn依赖于useIsUserSubscribed ,但useIsUserSubscribed不依赖于SubscribeUnsubscribeBtn中的任何内容。 Instead, useIsUserSubscribed is keeping a local state.相反, useIsUserSubscribed保留了本地 state。 You have a couple of choices here:您在这里有几个选择:

  1. Move the state regarding whetehr user is subscribed or not one level up, since you are using Redux, perhaps in Redux.由于您使用的是 Redux,可能在 Redux 中,移动 state 关于用户是否订阅的问题。
  2. Communicate to useIsUserSubscribed that you need to change its internal state.沟通使用useIsUserSubscribed需要更改其内部state。

For 1)对于1)

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

move this state to Redux store and use it with useSelector.将此 state 移动到 Redux 存储并与 useSelector 一起使用。

For 2) , return an array of value and callback from the hook, instead of just the value.对于2) ,从钩子返回一个值和回调数组,而不仅仅是值。 It will allow you to communicate from component back into the hook.它将允许您从组件通信回钩子。

In useIsUserSubscribed ,useIsUserSubscribed中,

  return [userIsSubscribed, setUserIsSubscribed];

Then in onClick , you can call setUserIsSubscribed(false) , changing the hook's internal state, and re-rendering your component.然后在onClick中,您可以调用setUserIsSubscribed(false) ,更改钩子的内部 state 并重新渲染您的组件。

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

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