我怎样才能使它成为一个包含钩子的可调用 utils 函数? [英] How can I make this a callable utils function that contains a hook?

查看:23
本文介绍了我怎样才能使它成为一个包含钩子的可调用 utils 函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用 Apollo Client 的 useQueryuseMutation 钩子在几个不同的组件中执行相同的功能.我不想重复逻辑,而是想创建一个辅助函数,将逻辑保存在一个地方.

问题在于,useQueryuseMutation 是钩子意味着它们不能作为函数存在于单独的文件中,而是需要是有效的 React 函数组件.

我开始尝试制作一些实用的东西,但开始认为这可能无法完成.

 导出函数 HandleFollow(props) {const [useFollowUser, { followData, followError }] = useMutation(FOLLOW_USER);useFollowUser({ variables: { fromId: auth().currentUser.uid, toId: "userIDxyz"}, onCompleted: props.handleUserFollow(user, isFollowingAuthor)})}

是否可以在 React Hooks 中使用 util/helper 函数?提前致谢!

在创建以下内容时,我收到 "无效的钩子调用.钩子只能在函数组件的主体内部调用." 我不知道为什么.

我认为这可能是由于在函数内部调用了 this,但我不知道如何避免这种情况.

导出函数 useHandleFollow(props) {const { user, isFollowingUser } = propsconst [useFollowUser, { followData, followError }] = useMutation(FOLLOW_USER);const [useUnfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);const [followSuccess, setFollowSuccess] = useState(false)如果(!isFollowingUser){useFollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: setFollowSuccess(true)})}别的{useUnfollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: setFollowSuccess(true)})}返回关注成功}

当我立即在我的组件中调用它(而不是从函数内部调用)时,它会继续无限地重新渲染:

这是组件中一些更完整的代码

在 Home.js 中:

const follow = useHandleFollow({ user: author, isFollowingAuthor })导出默认帖子 = (props) =>{让 { 发布,excludeUser = false } = props让{作者}=发布让 { 日期,事物,isFollowingAuthor } = 发布让 { profile_picture } = 作者让 currentUser = auth().currentUserconst [followed] = useHandleFollow({ 用户:作者,isFollowingAuthor })返回 ()}

这是在 follow.js 中,我将其导入为 import { useHandleFollow } from...

导出函数 useHandleFollow(props) {const { user, isFollowingUser } = propsconst [followUser, {followData, followError }] = useMutation(FOLLOW_USER);const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);const [followSuccess, setFollowSuccess] = useState(false)如果(!isFollowingUser){followUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: () => setFollowSuccess(true)})}别的{unfollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: () => setFollowSuccess(true)})}返回 [关注成功]}

解决方案

当然可以!您可以创建自己的自定义反应钩子.React hooks 使用了一个相当简单的命名约定,use-";前缀.

导出函数 useHandleFollow(props) {const [useFollowUser, { followData, followError }] = useMutation(FOLLOW_USER);使用关注用户({变量:{ fromId: auth().currentUser.uid, toId: "userIDxyz";},onCompleted: props.handleUserFollow(user, isFollowingAuthor)});}

仅从 React 函数调用钩子

<块引用>

不要从常规 JavaScript 函数调用 Hook.相反,您可以:

  • ✅ 从 React 函数组件调用 Hook.
  • ✅ 调用 Hooks from自定义 Hooks(我们将在下一页了解它们在下一页).

通过遵循此规则,您可以确保所有有状态的逻辑都在一个组件在其源代码中清晰可见.

下一页是提取自定义钩子 部分!

<块引用>

自定义 Hook 是名称以use"开头的 JavaScript 函数这可能会调用其他 Hook.

编辑

<块引用>

仍然收到无效的钩子使用.

导出函数 useHandleFollow(props) {const { user, isFollowingUser } = props;const [useFollowUser, { followData, followError }] = useMutation(FOLLOW_USER);const [useUnfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);const [followSuccess, setFollowSuccess] = useState(false);如果(!isFollowingUser){使用关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});} 别的 {使用取消关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});}返回 followSuccess;}

我认为这里的问题是mutate"的命名.函数,您已经不方便地将其命名为另一个 React Hook.React Hook linting 规则将 if (!isFollowingUser) {...} 解释为有条件地调用一个钩子,这是无效的.给他们一个非 React 钩子的名字.useFollowUserfollowUseruseUnfollowUserunfollowUser.

导出函数 useHandleFollow(props) {const { user, isFollowingUser } = props;const [followUser, { followData, followError }] = useMutation(FOLLOW_USER);const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);const [followSuccess, setFollowSuccess] = useState(false);如果(!isFollowingUser){关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});} 别的 {取消关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});}返回 followSuccess;}

<块引用>

仍然呈现循环

useHandleFollow 在每个渲染周期被调用.我认为您的自定义钩子会在无条件更新每个逻辑分支中的 followSuccess 状态时触发渲染循环.

导出函数 useHandleFollow(props) {...const [followSuccess, setFollowSuccess] = useState(false);//setFollowSuccess(true) 总是被调用如果(!isFollowingUser){关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});} 别的 {取消关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});}...}

每次调用钩子时都会更新状态.你应该在你希望这个效果何时运行上设置一个条件,即将那个代码块放在一个useEffect钩子中,并在isFollowingUser上有一个回调依赖>.效果回调只会在 isFollowingUser 值改变时运行,不会在组件渲染时运行.

导出函数 useHandleFollow(props) {const { user, isFollowingUser } = props;const [followUser, { followData, followError }] = useMutation(FOLLOW_USER);const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);const [followSuccess, setFollowSuccess] = useState(false);useEffect(() => {如果(!isFollowingUser){关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});} 别的 {取消关注用户({变量:{ fromId: auth().currentUser.uid, toId: user.id },onCompleted: setFollowSuccess(true)});}}, [isFollowingUser]);返回 followSuccess;}

<块引用>

我是否应该返回一些东西,比如 set 方法之类的东西,接受新道具?否则,我如何最终调用钩子那些新道具?

如果需要,您只需要从钩子返回更新程序函数.React 钩子在每个渲染周期都会被调用,因此如果您将 props 传递给它们,那么它们将始终接收当前的 props.

I'm currently using Apollo Client's useQuery and useMutation hooks to perform the same function in several different components. Instead of repeating the logic, I would like to create a helper function that can keep the logic in a single place.

The problem with this, is useQuery and useMutation being hooks mean they can't just live in a separate file as functions, but instead need to be valid React function components.

I started trying to make something functional but starting to think this might not be able to be done.

  export function HandleFollow (props) {
      const [useFollowUser, { followedData, followError }] = useMutation(FOLLOW_USER);
      useFollowUser({ variables: { fromId: auth().currentUser.uid, toId: "userIDxyz"}, onCompleted: props.handleUserFollow(user, isFollowingAuthor)})
  }

Is it possible to use util/helper functions with React Hooks? Thanks in advance!

When creating the below, I'm receiving "Invalid hook call. Hooks can only be called inside of the body of a function component." and I can't figure out why.

I think that may be due to calling this from inside a function, but I can't figure out how to avoid that.

export function useHandleFollow(props) {
  const { user, isFollowingUser } = props
  const [useFollowUser, { followedData, followError }] = useMutation(FOLLOW_USER);
  const [useUnfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);
  const [followSuccess, setFollowSuccess] = useState(false)
  
  if(!isFollowingUser){
    useFollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: setFollowSuccess(true)})
  }else{
    useUnfollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: setFollowSuccess(true)})
  }
  return followSuccess
}

And when I call this right away in my component (instead of calling from inside a function), it continues to infinitely re-render:

Here's some more complete code from the component

in Home.js:

const followed = useHandleFollow({ user: author, isFollowingAuthor })


export default  posts = (props) =>{
  let { post, excludeUser = false } = props
  let { author } = post
  let { date, things, isFollowingAuthor } = post
  let { profile_picture } = author
  let currentUser = auth().currentUser
  
  const [followed] = useHandleFollow({ user: author, isFollowingAuthor })
  
  return ()
}

And this is in follow.js, which I'm importing as import { useHandleFollow } from...

export function useHandleFollow(props) {
  const { user, isFollowingUser } = props
  const [followUser, { followedData, followError }] = useMutation(FOLLOW_USER);
  const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(UNFOLLOW_USER);
  const [followSuccess, setFollowSuccess] = useState(false)
  if(!isFollowingUser){
    followUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: () => setFollowSuccess(true)})
  }else{
    unfollowUser({ variables: { fromId: auth().currentUser.uid, toId: user.id}, onCompleted: () => setFollowSuccess(true)})
  }
  return [followSuccess]
}

解决方案

Of course it is! You can create your own custom react hooks. React hooks use a rather simple naming convention, "use-" prefix.

export function useHandleFollow(props) {
  const [useFollowUser, { followedData, followError }] = useMutation(
    FOLLOW_USER
  );
  useFollowUser({
    variables: { fromId: auth().currentUser.uid, toId: "userIDxyz" },
    onCompleted: props.handleUserFollow(user, isFollowingAuthor)
  });
}

Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions. Instead, you can:

  • ✅ Call Hooks from React function components.
  • ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).

By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

On the next page is an Extracting a Custom Hook section!

A custom Hook is a JavaScript function whose name starts with "use" and that may call other Hooks.

Edit

Still receiving invalid hook usage.

export function useHandleFollow(props) {
  const { user, isFollowingUser } = props;
  const [useFollowUser, { followedData, followError }] = useMutation(
    FOLLOW_USER
  );
  const [useUnfollowUser, { unfollowedData, unfollowedError }] = useMutation(
    UNFOLLOW_USER
  );
  const [followSuccess, setFollowSuccess] = useState(false);

  if (!isFollowingUser) {
    useFollowUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  } else {
    useUnfollowUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  }
  return followSuccess;
}

I think the issue here is the naming of the "mutate" function, which you've inconveniently named just like another React Hook. The React Hook linting rules are interpreting the if (!isFollowingUser) {...} as conditionally calling a hook, which is invald. Give them a non-React-hook name. useFollowUser to followUser and useUnfollowUser to unfollowUser.

export function useHandleFollow(props) {
  const { user, isFollowingUser } = props;
  const [followUser, { followedData, followError }] = useMutation(
    FOLLOW_USER
  );
  const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(
    UNFOLLOW_USER
  );
  const [followSuccess, setFollowSuccess] = useState(false);

  if (!isFollowingUser) {
    followUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  } else {
    unfollowUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  }
  return followSuccess;
}

Still render looping

useHandleFollow is called each render cycle. I think your custom hook triggers the render looping when it is unconditionally updating the followSuccess state in each branch of logic.

export function useHandleFollow(props) {
  ...
  const [followSuccess, setFollowSuccess] = useState(false);

  // setFollowSuccess(true) called always
  if (!isFollowingUser) {
    followUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  } else {
    unfollowUser({
      variables: { fromId: auth().currentUser.uid, toId: user.id },
      onCompleted: setFollowSuccess(true)
    });
  }
  ...
}

This ends up updating state every time the hook is called. You should place a condition on when you want this effect to be run, i.e. place that code block in an useEffect hook with a callback dependency on isFollowingUser. The effect callback will only run when the isFollowingUser value changes, not whenever the component renders.

export function useHandleFollow(props) {
  const { user, isFollowingUser } = props;
  const [followUser, { followedData, followError }] = useMutation(
    FOLLOW_USER
  );
  const [unfollowUser, { unfollowedData, unfollowedError }] = useMutation(
    UNFOLLOW_USER
  );
  const [followSuccess, setFollowSuccess] = useState(false);

  useEffect(() => {
    if (!isFollowingUser) {
      followUser({
        variables: { fromId: auth().currentUser.uid, toId: user.id },
        onCompleted: setFollowSuccess(true)
      });
    } else {
      unfollowUser({
        variables: { fromId: auth().currentUser.uid, toId: user.id },
        onCompleted: setFollowSuccess(true)
      });
    }
  }, [isFollowingUser]);

  return followSuccess;
}

Should I be returning something, like a set method or something, to accept new props? Otherwise, how do I end up calling the hook with those new props?

You only need to return updater function from hooks if you need them. React hooks are called each render cycle, so if you pass props to them then they will always receive the current props.

这篇关于我怎样才能使它成为一个包含钩子的可调用 utils 函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆