Swr 的缓存更新,但 UI 不会无缘无故地更新 - swr hook Next.js(使用打字稿)
Swr's cache updates but UI doesn't for no reason - swr hook Next.js ( with typescript )
我正在做一个 facebook 克隆,每次我按赞的按钮,我想立即看到变化,这是 swr 提供的,但是,它只在 4-8 秒后更新:/
我尝试做的是以下:当我点击赞按钮时,我首先改变 swr 提供的缓存,然后调用 API,然后重新验证数据以查看数据是否正确,实际上我控制台记录缓存并立即更新,但 UI 没有,我不知道为什么
让我用我的代码给你一些上下文
这是我的出版物的样子(在 pub 里面是这样 属性 )
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 发布的喜欢,这就是发生的事情
标识符被添加到缓存中,意味着用户喜欢该酒吧,但 UI 没有更新,更新需要 4 - 7 秒,可能更多,同样的事情发生在去掉类似的,看这个
剪掉了,但是,UI没有更新
我很绝望,我已经尝试了所有方法,我已经尝试修复这个问题将近一个星期了,但什么也没发现,我做错了什么,这是一个错误吗?
我认为问题在于您正在直接变异(在 javascript 而非 swr 意义上)swr 完全不可见的 swr 数据。只有当 return 从 API 得到响应时,你的状态才会更新,最终触发 swr 的观察者。
在这里您可能会注意到 currentPub.likes
是 currentPub
对象中的一个数组(引用)。您直接对其进行变异(使用 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
您应该重写它以排除直接突变,并且总是 return 更改引用以更改 objects/arrays。类似于:
(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
分支变异函数执行相同的操作。
我正在做一个 facebook 克隆,每次我按赞的按钮,我想立即看到变化,这是 swr 提供的,但是,它只在 4-8 秒后更新:/
我尝试做的是以下:当我点击赞按钮时,我首先改变 swr 提供的缓存,然后调用 API,然后重新验证数据以查看数据是否正确,实际上我控制台记录缓存并立即更新,但 UI 没有,我不知道为什么
让我用我的代码给你一些上下文
这是我的出版物的样子(在 pub 里面是这样 属性 )
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 发布的喜欢,这就是发生的事情
标识符被添加到缓存中,意味着用户喜欢该酒吧,但 UI 没有更新,更新需要 4 - 7 秒,可能更多,同样的事情发生在去掉类似的,看这个
剪掉了,但是,UI没有更新
我很绝望,我已经尝试了所有方法,我已经尝试修复这个问题将近一个星期了,但什么也没发现,我做错了什么,这是一个错误吗?
我认为问题在于您正在直接变异(在 javascript 而非 swr 意义上)swr 完全不可见的 swr 数据。只有当 return 从 API 得到响应时,你的状态才会更新,最终触发 swr 的观察者。
在这里您可能会注意到 currentPub.likes
是 currentPub
对象中的一个数组(引用)。您直接对其进行变异(使用 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
您应该重写它以排除直接突变,并且总是 return 更改引用以更改 objects/arrays。类似于:
(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
分支变异函数执行相同的操作。