如何解决Recoil JS 组件在没有状态更新的情况下重新渲染
问题已解决:归结为状态更新时缺少传播运算符?。
如果您找到此线程并且正在寻找包含过滤的 Recoil 分页实现,那么我用来定位问题的有效 CodeSandBox 示例位于此处:https://codesandbox.io/s/muddy-snow-bguux?file=/pages/index.js。
我一直在尝试使用 Recoil 进行分页,但被卡住了。似乎正在发生的事情是我订阅的组件正在重新渲染而没有更新的状态。
在我的索引页面上,我使用 NextJS SSR 从 Firebase 获取初始负载。该负载作为道具传递给页面组件,然后作为数组存储在 Recoil 原子中。
内容使用 JobFeed 组件呈现,该组件接收 Recoil 选择器,该选择器返回状态的过滤版本。这一切都很好。
const Index = ({initialJobs}) => {
const [jobs,setJobs] = useRecoilState(jobState);
const jobList = useRecoilValue(filteredJobListState);
const fallback = <div>Loading</div>
useEffect(() => {
if (jobs.length < 1) {
setJobs(initialJobs)
} else {
setJobs(jobList)
}
},[])
return (
<>
<Layout>
<Head>
<title>{SITE_TAGLINE} - {SITE_NAME}</title>
</Head>
<Masthead />
<Filters />
{
(jobList.length < 1) ? <div>Loading</div> :
<JobFeed jobs={jobList}/>
}
</Layout>
</>
)
}
反冲原子看起来像这样:
export const jobState = atom({
key: "jobState",default: [],});
过滤后的状态选择器看起来像这样:
export const filteredJobListState = selector({
key: 'filteredJobListState',get: ({get}) => {
// Get current filters
const filters = get(filterState);
// Get current job state
const jobList = get(jobState);
// Construct queries to return correct state object
if (!filters || filters.category === "" || filters.state === "") {
return jobList;
} else {
return jobFilter(jobList,filters);
};
// Check key value pairs and return filtered state
function jobFilter(jobs,query) {
return jobs.filter(job => {
for (let key in query) {
if (job[key] === undefined || job[key] != query[key])
return false;
}
return true;
});
};
}
});
const JobFeed = ({ title,jobs }) => {
const [loading,setLoading] = useRecoilState(loadingState);
const [postsEnd,setPostsEnd] = useRecoilState(postsEndState);
const [jobList,setJobList] = useRecoilState(jobState);
const getMorePosts = async () => {
setLoading(true);
// Get last post from current batch to run paginated query
const last = jobList[jobList.length - 1];
// Convert timestap for query if the format is incompatible
const cursor = typeof last.createdAt === 'number' ? fromMillis(last.createdAt) : last.createdAt;
const query = database
.collection('jobs')
.orderBy('createdAt','desc')
.startAfter(cursor)
.limit(20);
// Run query to get additional posts
try {
const newJobs = (await query.get()).docs.map(postToJSON);
setJobList([...jobList,newJobs]);
// The result of the console.log is the prevIoUs job state
console.log(jobList)
// Turn off loader
setLoading(false);
// Check to see if more posts available
if (newJobs.length < 20) {
setPostsEnd(true)
}
} catch (error) {
console.log(error)
}
}
return (
<section className="py-4">
<Container>
<Sectionheading>{title}</Sectionheading>
{jobs.map((job) => {
return (
<JobCard
key={job.id}
company={job.companyName}
position={job.position}
city={job.city}
state={job.state}
createdAt={job.createdAt}
category={job.category}
apply={job.application}
logo={job.companylogo}
/>
)
})}
<div className="border-t border-gray-100 my-8"></div>
{!loading && !postsEnd && <button className="btn-green" onClick={getMorePosts}>Load more</button>}
<Loader show={loading} />
{postsEnd && "You've reached the end!"}
</Container>
</section>
)
}
jobState 和 filterState 原子是 filtersJobState 选择器的依赖项。使用 useRecoilState 设置 filterState 会触发 JobFeed 组件的重新渲染,没有任何问题(但它是同步的)。
但是,当 async getMorePosts 函数更新 jobState 时,组件会重新渲染并抛出错误“TypeError: Cannot read property '0' of undefined。”
当我从 getMorePosts 调用 setState 之后,在 console.log jobState 之后,我会收到来自前一个状态的值。
我试过了:
- 将 JobFeed 组件包装在 Suspense 组件中,并检查 SSR。同样的问题。
- 重构事物以使用 getRecoilValueLoadable。同样的问题。
- 将 getMorePosts 逻辑放入选择器中。同样的问题。
- 将 getMorePosts 逻辑放入 useRecoilCallback。抛出嵌套钩子错误。
我错过了什么?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。