React useState 钩子在 useEffect 钩子内没有按预期工作 [英] React useState hook is not working as expected inside useEffect hook

查看:55
本文介绍了React useState 钩子在 useEffect 钩子内没有按预期工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:请不要将此标记为重复.我看过类似的问题和答案.但他们都没有为我工作.我只是在学习 React.

我想要实现的基本上是无限滚动.这样当用户滚动到页面末尾时,就会加载更多数据.

我使用 scroll eventListener 来实现这一点.它正在工作.

但是我面临着变量状态的问题.

首先,我已将loading 状态更改为true.然后获取数据并将状态设置为false.

第二,当滚动到页面末尾时,我再次将loading 状态更改为true.添加 1pageNo.然后再次获取数据并将loading 状态设置为false.

问题在于:

  1. loading 状态以某种方式保持真实.
  2. 更改 pageNo 状态不起作用.pageNo 始终保持为 1.
  3. 实际上,没有一个州按预期工作.

我的目标:(顺序)

  1. loading 设置为 true.

  2. 在组件初始化后从 API 中获取 10 个帖子.

  3. loading设置为false.

  4. 在用户滚动页面末尾后,用 pageNo 加 1.

  5. 重复第 1 步到第 3 步,直到所有帖子都加载完毕.

  6. 从 API 获得空响应后,将 allPostsLoaded 设置为 true.

我尝试过的:

我尝试将所有状态添加到 useEffect 钩子的 dependencyList 数组中.但随后发生了无限循环.

我也试过只将 pageNoloading 状态添加到数组中,但发生了同样的无限循环.

来源:

import React, { lazy, useState } from 'react';从'./Home.styles'导入{ PostSection};从 '../../models/BlogPost' 导入 { BlogPost };从 '../../services/PostService' 导入 { PostService };const defaultPosts: BlogPost[] = [{id: 'asdfg',内容:'这是演示内容',Title: '演示标题',部分:[],副标题: '',阅读时间:1,创建日期:新日期()}];const defaultPageNo = 1;const PostCardComponent = lazy(() => import('./../PostCard/PostCard'));const postService = new PostService();const Home = (props: any) =>{const [posts, setPosts]: [BlogPost[], (posts: BlogPost[]) =>void] = useState(defaultPosts);const [pageNo, setPageNo] = useState(defaultPageNo);const [pageSize, setPageSize] = useState(10);const [loading, setLoading] = useState(false);const [allPostsLoaded, setAllPostsLoaded] = useState(false);const [featuredPost, setFeaturedPost]: [BlogPost, (featuredPost: BlogPost) =>void] = useState(defaultPosts[0]);异步函数 getPosts() {return await postService.getPosts(pageSize, pageNo);}异步函数 getFeaturedPost() {返回等待 postService.getFeaturedPost();}函数handleScroll(事件:任何){console.log('加载' + 加载);console.log('allPostsLoaded' + allPostsLoaded);var target = event.target.scrollingElement;if (!loading && !allPostsLoaded && target.scrollTop + target.clientHeight === target.scrollHeight) {设置加载(真);setPageNo(pageNo => pageNo + 1);setTimeout(()=>{获取帖子().then(响应 => {const newPosts = response.data.data;设置加载(假);如果(newPosts.length){const temp = [ ...帖子];newPosts.forEach(post => !temp.map(m => m.Id).includes(post.Id) ? temp.push(post) : null);setPosts(temp);} 别的 {setAllPostsLoaded(true);}})}, 1000);}}函数初始化(){设置加载(真);Promise.all([getFeaturedPost(), getPosts()]).然后(回复 =>{设置加载(假);setFeaturedPost(responses[0].data.data);setPosts(responses[1].data.data);});}React.useEffect(() => {window.addEventListener(scroll", handleScroll);在里面();返回 () =>{window.removeEventListener(scroll", handleScroll);};}, []);返回 (<PostSection className="px-3 py-5 p-md-5"><div className="容器"><div className="item mb-5">{posts.map(post => (<明信片组件键={post.Id}标题={post.Title}介绍={post.Content}Id={post.Id}阅读时间={post.ReadTime}CreatedDate={post.CreatedDate}/>))}

</PostSection>);};导出默认首页;

解决方案

使用了更多的效果来处理pageNoloaderallPostsLoaded 状态对我有用.

更新来源:

import React, { lazy, useState } from 'react';import { Guid } from guid-typescript";从'./Home.styles'导入{ PostSection};从 '../../models/BlogPost' 导入 { BlogPost };从 '../../services/PostService' 导入 { PostService };从'antd'导入{骨架};const defaultPosts: BlogPost[] = [{编号:'456858568568568',内容:'这是演示内容.本来可以有更多的内容.',Title: '这是一个演示标题',部分:[],副标题: '',阅读时间:1,创建日期:新日期()}];const defaultPageNo = 1;const defaultPageSize = 10;const PostCardComponent = lazy(() => import('./../PostCard/PostCard'));const postService = new PostService();const 主页:React.FC= 道具 =>{const [posts, setPosts]: [BlogPost[], (posts: BlogPost[]) =>void] = useState(defaultPosts);const [pageNo, setPageNo] = useState(defaultPageNo);const [pageSize, setPageSize] = useState(defaultPageSize);const [loading, setLoading] = useState(false);const [allPostsLoaded, setAllPostsLoaded] = useState(false);const [featuredPost, setFeaturedPost]: [BlogPost, (featuredPost: BlogPost) =>void] = useState(defaultPosts[0]);函数 getNewGuid() {返回 Guid.create().toString();}异步函数 getPosts() {return await postService.getPosts(pageSize, pageNo);}异步函数 getFeaturedPost() {返回等待 postService.getFeaturedPost();}函数初始化(){设置加载(真);Promise.all([getFeaturedPost(), getPosts()]).然后(回复 =>{设置加载(假);setFeaturedPost(responses[0].data.data);setPosts(responses[1].data.data);});}React.useEffect(() => {在里面();返回;}, []);React.useEffect(() => {if (allPostsLoaded || loading) 返回;函数handleScroll(事件:任何){var target = event.target.scrollingElement;if (!loading && !allPostsLoaded && target.scrollTop + target.clientHeight === target.scrollHeight) {setPageNo(pageNo => pageNo+1);}}window.addEventListener(scroll", handleScroll);返回 () =>{window.removeEventListener(scroll", handleScroll);};}, [正在加载,所有帖子已加载]);React.useEffect(() => {如果 (pageNo > 1) {设置加载(真);setTimeout(()=>{获取帖子().then(响应 => {const newPosts = response.data.data;setTimeout(()=>{设置加载(假);如果(newPosts.length){const temp = [ ...帖子];newPosts.forEach(post => !temp.map(m => m.Id).includes(post.Id) ? temp.push(post) : null);setPosts(temp);} 别的 {setAllPostsLoaded(true);}}, 1000);})}, 1000);}}, [页面编号]);返回 (<PostSection className="px-3 py-5 p-md-5"><div className="容器"><div className="item mb-5">{posts.map(post => (<明信片组件键={post.Id}标题={post.Title}介绍={post.Content}Id={post.Id}阅读时间={post.ReadTime}CreatedDate={post.CreatedDate}/>))}

</PostSection>);};导出默认首页;

Disclaimer: Please don't mark this as duplicate. I've seen similar questions with answers. But none of them is working for me. I'm just learning React.

What I'm trying to achieve is basically infinite scrolling. So that when a user scrolls to the end of the page, more data will load.

I've used scroll eventListener to achieve this. And it is working.

But I'm facing problems with the state of the variables.

First, I've changed the loading state to true. Then fetch data and set the state to false.

Second, when scrolling to the end of the page occurs, I again change the loading state to true. Add 1 with pageNo. Then again fetch data and set the loading state to false.

The problems are:

  1. loading state somehow remains true.
  2. Changing the pageNo state is not working. pageNo always remains to 1.
  3. And actually none of the states are working as expected.

My goal: (Sequential)

  1. Set loading to true.

  2. Fetch 10 posts from API after component initialization.

  3. Set loading to false.

  4. After the user scrolls end of the page, add 1 with pageNo.

  5. Repeat Step 1 to Step 3 until all posts loaded.

  6. After getting an empty response from API set allPostsLoaded to true.

What I've tried:

I've tried adding all the states into dependencyList array of useEffect hook. But then an infinite loop occurs.

I've also tried adding only pageNo and loading state to the array, but same infinite loop occurs.

Source:

import React, { lazy, useState } from 'react';
import { PostSection } from './Home.styles';
import { BlogPost } from '../../models/BlogPost';
import { PostService } from '../../services/PostService';

const defaultPosts: BlogPost[] = [{
    Id: 'asdfg',
    Content: 'Hi, this is demo content',
    Title: 'Demo title',
    sections: [],
    subTitle: '',
    ReadTime: 1,
    CreatedDate: new Date()
}];

const defaultPageNo = 1;
const PostCardComponent = lazy(() => import('./../PostCard/PostCard'));
const postService = new PostService();

const Home = (props: any) => {
    const [posts, setPosts]: [BlogPost[], (posts: BlogPost[]) => void] = useState(defaultPosts);
    const [pageNo, setPageNo] = useState(defaultPageNo);
    const [pageSize, setPageSize] = useState(10);
    const [loading, setLoading] = useState(false);
    const [allPostsLoaded, setAllPostsLoaded] = useState(false);
    const [featuredPost, setFeaturedPost]: [BlogPost, (featuredPost: BlogPost) => void] = useState(defaultPosts[0]);

    async function getPosts() {
        return await postService.getPosts(pageSize, pageNo);
    }

    async function getFeaturedPost() {
        return await postService.getFeaturedPost();
    }

    function handleScroll(event: any) {
        console.log('loading ' + loading);
        console.log('allPostsLoaded ' + allPostsLoaded);
        var target = event.target.scrollingElement;
        if (!loading && !allPostsLoaded && target.scrollTop + target.clientHeight === target.scrollHeight) {
            setLoading(true);
            setPageNo(pageNo => pageNo + 1);
            setTimeout(()=>{
                getPosts()
                    .then(response => {
                        const newPosts = response.data.data;
                        setLoading(false);
                        if (newPosts.length) {
                            const temp = [ ...posts ];
                            newPosts.forEach(post => !temp.map(m => m.Id).includes(post.Id) ? temp.push(post) : null);
                            setPosts(temp);
                        } else {
                            setAllPostsLoaded(true);
                        }
                    })
            }, 1000);
        }
    }

    function init() {
        setLoading(true);
        Promise.all([getFeaturedPost(), getPosts()])
            .then(
                responses => {
                    setLoading(false);
                    setFeaturedPost(responses[0].data.data);
                    setPosts(responses[1].data.data);
                }
            );
    }

    React.useEffect(() => {
        window.addEventListener("scroll", handleScroll);
        init();
        return () => {
            window.removeEventListener("scroll", handleScroll);
        };
      }, []
    );
    
    return (
        <PostSection className="px-3 py-5 p-md-5">
            <div className="container">
                <div className="item mb-5">
                    {posts.map(post => (
                        <PostCardComponent
                            key={post.Id}
                            Title={post.Title}
                            intro={post.Content}
                            Id={post.Id}
                            ReadTime={post.ReadTime}
                            CreatedDate={post.CreatedDate}
                        />
                    ))}
                </div>
            </div>
        </PostSection>
    );
};

export default Home;

解决方案

Used more effects to handle the change of pageNo, loader and allPostsLoaded state worked for me.

Updated Source:

import React, { lazy, useState } from 'react';
import { Guid } from "guid-typescript";
import { PostSection } from './Home.styles';
import { BlogPost } from '../../models/BlogPost';
import { PostService } from '../../services/PostService';
import { Skeleton } from 'antd';

const defaultPosts: BlogPost[] = [{
    Id: '456858568568568',
    Content: 'Hi, this is demo content. There could have been much more content.',
    Title: 'This is a demo title',
    sections: [],
    subTitle: '',
    ReadTime: 1,
    CreatedDate: new Date()
}];
const defaultPageNo = 1;
const defaultPageSize = 10;
const PostCardComponent = lazy(() => import('./../PostCard/PostCard'));
const postService = new PostService();

const Home: React.FC<any> = props => {
    const [posts, setPosts]: [BlogPost[], (posts: BlogPost[]) => void] = useState(defaultPosts);
    const [pageNo, setPageNo] = useState(defaultPageNo);
    const [pageSize, setPageSize] = useState(defaultPageSize);
    const [loading, setLoading] = useState(false);
    const [allPostsLoaded, setAllPostsLoaded] = useState(false);
    const [featuredPost, setFeaturedPost]: [BlogPost, (featuredPost: BlogPost) => void] = useState(defaultPosts[0]);

    function getNewGuid() {
        return Guid.create().toString();
    }

    async function getPosts() {
        return await postService.getPosts(pageSize, pageNo);
    }

    async function getFeaturedPost() {
        return await postService.getFeaturedPost();
    }

    function init() {
        setLoading(true);
        Promise.all([getFeaturedPost(), getPosts()])
            .then(
                responses => {
                    setLoading(false);
                    setFeaturedPost(responses[0].data.data);
                    setPosts(responses[1].data.data);
                }
            );
    }

    React.useEffect(() => {
        init();
        return;
    }, []);

    React.useEffect(() => {
        if (allPostsLoaded || loading) return;

        function handleScroll(event: any) {
            var target = event.target.scrollingElement;
            if (!loading && !allPostsLoaded && target.scrollTop + target.clientHeight === target.scrollHeight) {
                setPageNo(pageNo => pageNo+1);
            }
        }

        window.addEventListener("scroll", handleScroll);
        return () => {
            window.removeEventListener("scroll", handleScroll);
        };
      }, [loading, allPostsLoaded]
    );

    React.useEffect(() => {
        if (pageNo > 1) {
            setLoading(true);
            setTimeout(()=>{
                getPosts()
                    .then(response => {
                        const newPosts = response.data.data;
                        setTimeout(()=>{
                            setLoading(false);
                            if (newPosts.length) {
                                const temp = [ ...posts ];
                                newPosts.forEach(post => !temp.map(m => m.Id).includes(post.Id) ? temp.push(post) : null);
                                setPosts(temp);
                            } else {
                                setAllPostsLoaded(true);
                            }
                        }, 1000);
                    })
            }, 1000);
        }
      }, [pageNo]
    );

    return (
        <PostSection className="px-3 py-5 p-md-5">
            <div className="container">
                <div className="item mb-5">
                    {posts.map(post => (
                        <PostCardComponent
                            key={post.Id}
                            Title={post.Title}
                            intro={post.Content}
                            Id={post.Id}
                            ReadTime={post.ReadTime}
                            CreatedDate={post.CreatedDate}
                        />
                    ))}
                </div>
            </div>
        </PostSection>
    );
};

export default Home;

这篇关于React useState 钩子在 useEffect 钩子内没有按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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