使用 redux 无限滚动

Infinite scrolling with redux

我遇到了这个问题。

我的后端有一个调用,它 returns 根据请求的页面为我提供了一个元素数组,为简单起见,假设它的结构如下

export const fetchPosts = (page) => API.get(`/posts?page=${page}`)

现在在我的操作中,当页面加载时,我只需调用我的端点(默认页面为 1)并获取我的帖子。这样:

export const getPosts = (page) => async (dispatch) => {
  try {
    
    dispatch({
      type: START_LOADING,
    });
    const { data } = await api.fetchPosts(
      page,
      store.getState().auth.user?._id
    );
      
    dispatch({
      type: GET_POSTS,
      payload: data,
    });
    dispatch({
      type: END_LOADING,
    });
  } catch (error) {
    console.log(error);
  }
};

最后是我的 reducer,它只是将值分配给我的初始帖子状态,如下所示:

const initialState = {
  posts: [],
  currentPage: null,
  numberOfPages: null,
  isLoading: true,
  post: {},
  postsOfCurrentUser: [],
};



case GET_POSTS:
      return {
        posts: payload.data,
        currentPage: payload.currentPage,
        numberOfPages: payload.numberOfPages,
        post: {},
      };

现在,我想尝试做的是,当您按下按钮或滚动时,会通过新页面向我的后端发出新调用,到目前为止没有问题。 当我想修改过去的有效载荷时,问题就出现了 为简单起见,我们假设是这样的: 初始页面加载时的帖子:

[{title : 1} , {title:2} , {title:3}]

按下按钮或滚动后我希望它是:

[{title : 1} , {title:2} , {title:3},{title : 4} , {title:5} , {title:6}]

所以我在行动中尝试了这样的操作:

export const getPosts = (page) => async (dispatch) => {
  try {
    dispatch({
      type: START_LOADING,
    });
    const { data } = await api.fetchPosts(
      page,
      store.getState().auth.user?._id
    );
    if (page === 1) {
      dispatch({
        type: GET_POSTS,
        payload: data,
      });
    } else {
      dispatch({
        type: GET_POSTS,
        payload: [...store.getState().posts.posts, data],
      });
    }

    dispatch({
      type: END_LOADING,
    });
  } catch (error) {
    console.log(error);
  }
};

但我得到的是:

[
   [
      {title : 1} , {title:2} , {title:3}
   ]
   ,{title : 4} , {title:5} , {title:6}
]

显然一切都会爆炸。我怎样才能做到这一点?有更好的方法吗?提前感谢谁会回答我。

我已经解决了这个问题,但我不知道这是否是一个很好的解决方案:

export const getPosts = (page) => async (dispatch) => {
  try {
    const { data } = await api.fetchPosts(
      page,
      store.getState().auth.user?._id
    );

    console.log("page action ==> ", page);
    // console.log("THIS IS DATA ==> ", data.data);

    if (page === 1) {
      dispatch({
        type: START_LOADING,
      });
      dispatch({
        type: GET_POSTS,
        payload: data,
      });
    } else {
      let newpayload = store.getState().posts.posts;
      data.data.map((item) => {
        return newpayload.push(item);
      });
    }

    dispatch({
      type: END_LOADING,
    });
  } catch (error) {
    console.log(error);
  }
};

您的 GET_POSTS 操作不需要包含整个帖子数组,包括那些已经处于该状态的帖子。您可以分派一个动作,其中包含页面的帖子数组和当前页码。 if (page === 1) 逻辑最好在 reducer 中处理。

我建议您分派一个操作,其中 payload 是一个具有属性 postspage 的对象。但是看看你的减速器,你的 API 中的 data 变量似乎已经在 .currentPage 属性 中包含了该信息。因此,您可以删除所有添加到动作创建者的额外逻辑,然后返回到您发布的第一个版本,其中所有页面都发送相同的动作:

dispatch({
  type: GET_POSTS,
  payload: data
});

(您可以使用 Redux Toolkit 中的 createAsyncThunk 函数使您的 thunk 操作更加清晰和简单。)

reducer 的工作是根据页码找出要做什么。

case GET_POSTS:
  const { data, currentPage, numberOfPages } = payload;
  return {
     ...state,
     posts: currentPage === 1 ? data : [...state.posts, ...data]
     currentPage,
     numberOfPages
  };

我正在使用三元运算符来替换整个帖子数组或添加到现有数组,具体取决于 currentPage 的值。