Swr 的缓存更新但 UI 不会无缘无故 - swr hook Next.js ( with typescript )

时间:2021-06-07 16:13:35

标签: reactjs typescript next.js swr

我正在做一个 facebook 克隆,每次我按下喜欢的按钮时,我想立即看到更改,这是 swr 提供的东西,但是,它只在 4-8 秒后更新:/

我试图做的是以下:当我点击喜欢的按钮时,我首先改变 swr 提供的缓存,然后我调用 API,然后重新验证数据以查看是否一切正常数据正确,实际上我控制台记录缓存并立即更新,但 UI 没有,我不知道为什么

让我给你一些关于我的代码的上下文

这就是我的出版物的样子(在 pub 里面是 likes 属性)


export type theLikes = {
  identifier: string;
};

export type theComment = {
  _id?: string;
  body: string;
  name: string;
  perfil?: string;
  identifier: string;
  createdAt: string;
  likesComments?: theLikes[];
};

export interface Ipublication {
  _id?: string;
  body: string;

  photo: string;

  creator: {
    name: string;
    perfil?: string;
    identifier: string;
  };

  likes?: theLikes[];

  comments?: theComment[];

  createdAt: string;
}

export type thePublication = {
  data: Ipublication[];
};

这是我要求所有带有 getStaticProps 的出版物

const PublicationsHome = ({ data: allPubs }) => {
  // All pubs

  const { data: Publications }: thePublication = useSWR(
    `${process.env.URL}/api/publication`,
    {
      initialData: allPubs,
      revalidateOnFocus: false
    }
  );


  return (
    <>
      {Publications ? (
        <>
          <PublicationsHomeHero>
            {/* Create pub */}
            <CreatePubs />
            {/* Show pub */}
            {Publications.map(publication => {
              return <Pubs key={publication._id} publication={publication} />;
            })}
          </PublicationsHomeHero>
        </>
      ) : (
        <div></div>
      )}
    </>
  );
};

export const getStaticProps: GetStaticProps = async () => {
  const { data } = await axios.get(`${process.env.URL}/api/publication`);

  return {
    props: data
  };
};

export default PublicationsHome;

这是点赞按钮的位置(重点是LikePub,逻辑就在这里)

条件很简单,如果用户已经点赞了,就点赞,否则,点赞这个贴吧


interface IlikesCommentsProps {
  publication: Ipublication;
}

const LikesComments: React.FC<IlikesCommentsProps> = ({ publication }) => {

const LikePub = async (): Promise<void> => {
    try {
      if (publication.likes.find(f => f.identifier === userAuth.user.id)) {
        mutate(
          `${process.env.URL}/api/publication`,
          (allPublications: Ipublication[]) => {
            const currentPub = allPublications.find(f => f === publication);

            const deleteLike = currentPub.likes.findIndex(
              f => f.identifier === userAuth.user.id
            );

            currentPub.likes.splice(deleteLike, 1);

            const updatePub = allPublications.map(pub =>
              pub._id === currentPub._id ? currentPub : pub
            );

            return updatePub;
          },
          false
        );
      } else {
        mutate(
          `${process.env.URL}/api/publication`,
          (allPublications: Ipublication[]) => {
            const currentPub = allPublications.find(f => f === publication);
            currentPub.likes.push({ identifier: userAuth.user.id });

            const updatePub = allPublications.map(pub =>
              pub._id === currentPub._id ? currentPub : pub
            );

            return updatePub;
          },
          false
        );
      }
      console.log(publication.likes);
      await like({ identifier: userAuth.user.id }, publication._id);
      mutate(`${process.env.URL}/api/publication`);
    } catch (err) {
      if (err) {
        mutate(`${process.env.URL}/api/publication`);
      }
    }
  };

return (

      <motion.div
          onClick={LikePub}
          variants={Actions}
          whileHover="whileHover"
          whileTap="whileTap"
        >
              <motion.span>
                <LikeIcon colorIcon="rgb(32, 120, 244)" />
              </motion.span>
              <LikeCommentText
                separation="0.2rem"
                colorText="rgb(32, 120, 244)"
              >
                Me gusta
              </LikeCommentText>
        </motion.div>

)
}

正如你所看到的,我在 console.log 发布的喜欢,这就是发生的事情

enter image description here

缓存中添加了标识符,表示用户喜欢了pub,但是UI没有更新,更新需要4-7秒,可能更多,删除like也发生了同样的事情,看看这个

enter image description here

等等,但是,UI 不会更新

我很绝望,我已经尝试了所有方法,我已经尝试解决这个问题将近一个星期了,但什么也没找到,我做错了什么,这是一个错误吗?

1 个答案:

答案 0 :(得分:1)

我相信问题在于您正在直接改变(在 javascript 而非 swr 意义上)swr 完全不可见的 swr 数据。并且只有当 API 返回响应时,您的状态才会更新并最终触发 swr 的观察者。


在这里您可能会注意到 currentPub.likescurrentPub 对象内的一个数组(引用)。您直接对其进行变异(使用 splice),然后将 相同 引用插入回 allPublications 对象。从 swr 的角度来看,likes 数组没有改变。它仍然保持与突变前相同的引用:

(allPublications: Ipublication[]) => {
    const currentPub = allPublications.find(f => f === publication);

    const deleteLike = currentPub.likes.findIndex(
        f => f.identifier === userAuth.user.id
    );

    currentPub.likes.splice(deleteLike, 1);
    const updatePub = allPublications.map(pub =>
        pub._id === currentPub._id ? currentPub : pub
    );

    return updatePub;
}

用于说明行为的代码段:

const allPublications = [{ attr: 'attr', likes: [1, 2, 3] }]
const currentPub = allPublications[0]
currentPub.likes.splice(1, 1)
const updatePub = allPublications.map((pub, idx) => idx === 0 ? currentPub :  pub)

console.log(updatePub[0] === allPublications[0]) // true
console.log(updatePub[0].likes === allPublications[0].likes) // true. the reference stays the same
console.log(updatePub[0]) // while the object has changed

您应该重写它以排除直接突变,并始终为更改的对象/数组返回更改的引用。类似的东西:

(allPublications: Ipublication[]) => {
    const currentPub = allPublications.find(f => f === publication);

    const likes = currentPub.likes.filter( // filter creates new array (reference)
        f => f.identifier !== userAuth.user.id
    );
    const updatePub = allPublications.map(pub => // map creates new array
        pub._id === currentPub._id ? { ...currentPub, likes } : pub // {} creates new object (reference)
    );

    return updatePub;
}

const allPublications = [{ attr: 'attr', likes: [1, 2, 3] }]
const currentPub = allPublications[0]

const likes = currentPub.likes.filter((el) => el !== 2)
const updatePub = allPublications.map((pub, idx) => 
  idx === 0 ? { ...currentPub, likes } :  pub
)

console.log(updatePub[0] === allPublications[0]) // false
console.log(updatePub[0].likes === allPublications[0].likes) // false
console.log(updatePub[0])

else 分支变异函数做同样的操作。