我怎样才能使它成为一个包含钩子的可调用实用程序函数?

How can I make this a callable utils function that contains a hook?

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

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

我开始尝试做一些功能性的东西,但开始认为这可能无法完成。

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

React Hooks 可以使用 util/helper 函数吗?提前致谢!

在创建以下内容时,我收到了 "Invalid hook call. Hooks can only be called inside of the body of a function component." 但我不明白为什么。

我认为这可能是由于从函数内部调用它造成的,但我不知道如何避免这种情况。

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
}

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

下面是组件的一些更完整的代码

在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 ()
}

这是在 follow.js 中,我将其导入为 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]
}

您可以创建一个 Custom Hook 来实现您的目标,方法是声明一个以 use 开头的函数,如下所示:

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

请记住,所有 React Hooks Rule 也适用于 Custom Hooks

当然是!您可以创建自己的自定义反应挂钩。 React hooks 使用相当简单的命名约定,“use-”前缀。

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.

下一页是 Extracting a Custom Hook 部分!

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

编辑

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

我认为这里的问题是“mutate”函数的命名,您给它命名很不方便,就像另一个 React Hook 一样。 React Hook linting 规则将 if (!isFollowingUser) {...} 解释为有条件地调用一个钩子,这是无效的。给他们一个非 React-hook 的名字。 useFollowUserfollowUseruseUnfollowUserunfollowUser

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 在每个渲染周期被调用。我认为您的自定义挂钩在无条件更新每个逻辑分支中的 followSuccess 状态时触发渲染循环。

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

这最终更新状态 每次 调用钩子时。您应该在希望此效果为 运行 时设置条件,即将 that 代码块放在 useEffect 挂钩中,回调依赖于 [=23] =].效果回调仅在 isFollowingUser 值更改时才会 运行,而不是在组件呈现时。

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?

如果需要,您只需要 return 从钩子中获取更新程序功能。 React hooks 在每个渲染周期被调用,所以如果你将 props 传递给它们,那么它们将始终收到当前的 props。