【问题标题】:React useState hook is not working as expected inside useEffect hookReact useState 钩子在 useEffect 钩子中没有按预期工作
【发布时间】:2021-03-22 02:41:09
【问题描述】:

免责声明:请不要将此标记为重复。我见过类似的问题和答案。但它们都不适合我。我只是在学习 React。

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

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

但是我遇到了变量状态的问题。

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

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

问题是:

  1. loading 状态以某种方式保持true
  2. 更改pageNo 状态无效。 pageNo 始终保留为 1
  3. 实际上没有一个州按预期工作。

我的目标:(顺序)

  1. loading 设置为true

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

  3. loading 设置为false

  4. 用户滚动页尾后,用pageNo加1。

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

  6. 从 API 集 allPostsLoadedtrue 获得空响应后。

我的尝试:

我尝试将所有状态添加到 useEffect 挂钩的依赖列表数组中。但随后发生了无限循环。

我也尝试只向数组添加 pageNoloading 状态,但同样发生了无限循环。

来源:

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;

【问题讨论】:

  • UseEffect 触发对其依赖项的更改,所以是的,如果您在 useEffect 块中更改 pageNo,将 pageNo 添加到您的部门将导致无限循环,您这样做.与loading 相同
  • 我不确定这是否是您的全部问题,但我怀疑您需要将 handleScroll 的定义移到 useEffect 块之外,因为一旦块落下,它可能会变得未定义超出范围。
  • @samuei,重点是我正在尝试更新 pageNoloading 属性。他们都没有被改变。 pageNo 始终保持为 1,loading 始终保持为真。
  • 请尝试将您的函数定义移到useEffect 块之外。
  • 您在第一次渲染时为useEffect 创建回调,并且您的pageNo 被烘焙到闭包中。因此它永远不会改变。但是 - 请原谅我的坦率 - 你的组件代码是相当复杂的混乱。你可能想清除它。古语有云:“看不清就做不好”。 (可能是孔子,公元 400 年)

标签: reactjs react-hooks react-typescript


【解决方案1】:

使用更多效果来处理pageNoloaderallPostsLoaded 状态的变化对我有用。

更新来源:

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;

【讨论】:

    猜你喜欢
    • 2021-11-11
    • 2021-09-21
    • 2020-09-01
    • 1970-01-01
    • 2021-10-24
    • 2020-09-02
    • 2020-05-23
    • 2020-02-19
    • 2022-12-08
    相关资源
    最近更新 更多