Максимальная глубина обновления превышена в React Infinite Scroll - PullRequest
1 голос
/ 29 сентября 2019

Я пытаюсь реализовать отложенную загрузку в приложении стека MERN, как в producthunt .Я хочу, чтобы сообщения, созданные в текущую дату, отображались по умолчанию.Если пользователь прокрутит страницу вниз, он получит больше данных за предыдущую дату.Я использую реакцию бесконечного свитка.Тем не менее, похоже, что приложение запрашивает API как бесконечный цикл без прослушивания прокрутки.Я получил следующую ошибку.Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Функция асинхронная / ожидание, поэтому я не понимаю, почему она продолжает вызывать новые запросы, даже если старый запрос еще не разрешен.

В компонентах Post

import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import PostItem from './PostItem';
import UserItem from '../users/UserItem';
import TopDiscussion from '../TopDiscussion';
import SmallAbout from '../SmallAbout';
import { getPostsByDate } from '../../actions/post';
import Moment from 'react-moment';
import InfiniteScroll from 'react-infinite-scroller';

const Posts = ({ getPostsByDate, post: { posts, loading } }) => {
  const now = new Date();
  const startOfToday = new Date(
    now.getFullYear(),
    now.getMonth(),
    now.getDate()
  );

  // startOfToday =  startOfToday -1
  useEffect(() => {
    getPostsByDate(startOfToday);
  }, [getPostsByDate]);

  const [date, setDate] = useState(startOfToday);
  const [shown, setShown] = useState();

  const getPosts = () => {
    getPostsByDate(date);
    let count = new Date(date);
    count.setDate(count.getDate() - 1);
    setDate(count);
  };

  return loading ? (
    <Spinner />
  ) : (
    <div className='main-grid'>
      <div className='posts-grid'>
        <h1 className='large text-primary'>Ideas</h1>
        <div className='posts'>
          <div className='post-dummy'>
            <InfiniteScroll
              dataLength={posts.length}
              pageStart={0}
              loadMore={getPosts}
              hasMore={posts && posts.length < 10}
              loader={
                <div className='loader' key={0}>
                  Loading ...
                </div>
              }
            >
              {posts
                .sort((a, b) =>
                  a.likes.length > b.likes.length
                    ? -1
                    : b.likes.length > a.likes.length
                    ? 1
                    : 0
                )
                .map(post => (
                  <PostItem key={post._id} post={post} />
                ))}
            </InfiniteScroll>
          </div>
        </div>
      </div>
      <div className='right-panel-grid'>
        <SmallAbout />
        <UserItem />
        <TopDiscussion posts={posts} />
        <div
          className='fb-group'
          data-href='https://www.facebook.com/groups/ideatoshare/'
          data-width='350'
          data-show-social-context='true'
          data-show-metadata='false'
        ></div>

        <iframe
          title='producthunt'
          style={{ border: 'none' }}
          src='https://cards.producthunt.com/cards/posts/168618?v=1'
          width='350'
          height='405'
          frameBorder='0'
          scrolling='no'
          allowFullScreen
        ></iframe>
      </div>
    </div>
  );
};

Posts.propTypes = {
  getPostsByDate: PropTypes.func.isRequired,
  post: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  post: state.post
});
export default connect(
  mapStateToProps,
  { getPostsByDate }
)(Posts);

Постредуктор

import {
  GET_POSTS,
  POST_ERROR,
  UPDATE_LIKES,
  UPDATE_LIKE,
  UPDATE_COMMENT_LIKES,
  DELETE_POST,
  ADD_POST,
  GET_POST,
  ADD_COMMENT,
  REMOVE_COMMENT,
  ADD_SUB_COMMENT,
  REMOVE_SUB_COMMENT,
  UPDATE_STATUS
} from '../actions/types';

const initialState = {
  posts: [],
  post: null,
  loading: true,
  error: {}
};

export default function(state = initialState, action) {
  const { type, payload } = action;

  switch (type) {
    case GET_POSTS:
      return {
        ...state,
        posts: [...state.posts, ...payload],
        // posts: payload,
        loading: false
      };
    case GET_POST:
      return {
        ...state,
        post: payload,
        loading: false
      };
    case ADD_POST:
      return {
        ...state,
        post: payload,
        // posts: [payload, ...state.posts],
        loading: false
      };

    case POST_ERROR:
      return {
        ...state,
        error: payload,
        loading: false
      };

    case UPDATE_COMMENT_LIKES:
      return {
        ...state,
        post: {
          ...state.post,
          comments: payload
        },
        loading: false
      };

    case UPDATE_LIKES:
      return {
        ...state,
        posts: state.posts.map(post =>
          post._id === payload.id ? { ...post, likes: payload.likes } : post
        ),
        loading: false
      };
    case UPDATE_LIKE:
      return {
        ...state,
        post: { ...state.post, likes: payload },
        loading: false
      };

    case UPDATE_STATUS:
      return {
        ...state,
        posts: state.posts.map(post =>
          post._id === payload.id ? { ...post, status: payload.status } : post
        ),
        loading: false
      };

    case DELETE_POST:
      return {
        ...state,
        posts: state.posts.filter(post => post._id !== payload),
        loading: false
      };
    case ADD_COMMENT:
      return {
        ...state,
        // payload is all the comments
        post: { ...state.post, comments: payload },
        loading: false
      };

    case ADD_SUB_COMMENT:
      return {
        ...state,
        // payload is all the comments of a post
        post: { ...state.post, comments: payload },
        loading: false
      };

    case REMOVE_COMMENT:
      return {
        ...state,
        post: {
          ...state.post,
          comments: state.post.comments.filter(
            comment => comment._id !== payload
          ),
          loading: false
        }
      };

    case REMOVE_SUB_COMMENT:
      return {
        ...state,
        post: {
          ...state.post,
          comments: payload
          // comments: state.post.comments.map(comment =>
          //   {
          //   if (comment._id === payload.commentId) {
          //     comment.subComments.filter(
          //       subcomment => subcomment._id === payload.subcommentId
          //     );
          //   }
          // }
          // )
        },
        loading: false
      };

    default:
      return state;
  }
}

Пост-действие

//GetTodayPost
export const getPostsByDate = date => async dispatch => {
  try {
    const res = await axios.get(`/api/posts/${date}`);
    dispatch({
      type: GET_POSTS,
      payload: res.data
    });
  } catch (err) {
    dispatch({
      type: POST_ERROR,
      payload: { msg: err.response.statusText, status: err.response.status }
    });
  }
};

Пост API

router.get('/:date', async (req, res) => {
  try {

    const startOfToday = new Date(req.params.date);
    const endOfToday = new Date(req.params.date);
    endOfToday.setDate(endOfToday.getDate() + 1);
    const posts = await Post.find({
      date: { $gte: startOfToday, $lte: endOfToday }
    }).sort({
      date: -1
    });
    res.json(posts);
  } catch (err) {
    console.error(err.message);
    res.send(500).send('Server Error');
  }
});

1 Ответ

0 голосов
/ 29 сентября 2019

Ладно .. поэтому после некоторого тестирования с InfiniteScroll это, похоже, происходит, потому что ваше свойство hasMore всегда равно true ... Вы должны указать некоторый тип условия, чтобы InfiniteScroll знал, когдаи когда нет, загрузите больше данных.

Я получил ту же ошибку, что и вы, перед добавлением проверки, которая сообщает InfiniteScroll, что больше нет данных для загрузки.

У меня естьпостроил следующий пример, чтобы показать, как использовать InfiniteScroll

Вы можете посмотреть живую демонстрацию здесь


PostsContainer.js

import React, { useState, useEffect } from "react";
import Posts from "./Posts";
import InfiniteScroll from "react-infinite-scroller";

const loadingStyle = {
  textAlign: "center",
  fontSize: "48px",
  color: "red"
};

function PostsContainer({ url, itemsToDisplay = 5 }) {
  const [data, setData] = useState();
  const [shownData, setShownData] = useState();

  useEffect(() => {
    (async () => {
      let items = await fetchPosts(url);
      let itemsToShow = selectNItems(items, itemsToDisplay);
      setShownData(itemsToShow);
      setData(items);
    })();
  }, [url]);

  async function fetchPosts(url) {
    let res = await fetch(url);
    return await res.json();
  }

  const selectNItems = (obj, n) => {
    return obj.slice(0, n);
  }

  const loadMorePosts = () => {
    let items =
      data &&
      shownData && 
      selectNItems(data, shownData.length + itemsToDisplay)
    setShownData(items);
  };

  return (
    <InfiniteScroll
      pageStart={0}
      loadMore={loadMorePosts}
      hasMore={data && shownData && data.length > shownData.length}
      loader={<div style={loadingStyle}>Loading ...</div>}
      useWindow={true}
    >
      <Posts posts={shownData} />
    </InfiniteScroll>
  );
}

export default PostsContainer;

Posts.js

import React from 'react';
import Post from './Post';

const headingStyle = {
  textAlign: 'center',
}

function Posts({ posts }) {
  return(
    <div>
      <h1 style={headingStyle}>Posts</h1>
      {posts && posts.length > 0 && posts.map((p, i) => <Post key={i} data={p} index={i} />)}
    </div>
  );
}

export default Posts;

Post.js

import React from "react";

const containerStyle = {
  border: "1px solid black",
  margin: "10px auto",
  maxWidth: "50vw",
  padding: '0px 10px 0px 0px'
};

const postHeaderStyle = {
  textAlign: "center",
  padding: "0px"
};

function Post({ data, index }) {
  return (
    <div style={containerStyle}>
      {index !== "" && <h3 style={postHeaderStyle}>Post #{index}</h3>}
      <ul>
        <li>
          <b>userId:</b> {data.userId}
        </li>
        <li>
          <b>id:</b> {data.id}
        </li>
        <li>
          <b>title:</b> {data.title}
        </li>
        <li>
          <b>body:</b> {data.body}
        </li>
      </ul>
    </div>
  );
}

export default Post;

index.js

import React from "react";
import { render } from "react-dom";
import PostsContainer from "./Components/PostsContainer";

function App() {
  return (
    <PostsContainer
      itemsToDisplay={5}
      url="https://jsonplaceholder.typicode.com/posts"
    />
  );
}

render(<App />, document.getElementById("root"));
...