【问题标题】:How to fetch data partially in react, redux?如何在反应中获取部分数据,redux?
【发布时间】:2021-07-02 22:56:55
【问题描述】:

动作

import { FETCH_BLOG, FETCH_BLOG_ERROR, FETCH_BLOG_LOADING } from "../constants/blogActionTypes"
    
    const initialState = {
        blogs: [],
    error: '',
    loading: false,
    allBlogs: []
}
// eslint-disable-next-line import/no-anonymous-default-export
export default (blogs = initialState, action) => {
    switch (action.type) {
        case FETCH_BLOG_LOADING:
            return {
                blogs: [...blogs.blogs],
                loading: true,
                error: ''
            };
        case FETCH_BLOG_ERROR:
            return {
                blogs: [...blogs.blogs],
                loading: false,
                error: action.payload
            };
        case FETCH_BLOG:
            return {
                blogs: [...action.payload, ...blogs.blogs],
                loading: false,
                error: ''
            };
        default: return blogs;
    }
}

减速器

export const fetchBlogs = (data) => async (dispatch) =>{

    dispatch({ type: FETCH_BLOG_LOADING, payload: true })
    fetch('http://localhost:5000/blog?show=' + data, {
        method: 'GET',
        headers: {
            authorization: userData.token
        }
    })
        .then(res => res.json())
        .then(data => {
            if (data.message) {
                dispatch(fetchBlogsError(data.message))
            } else {
                dispatch({ type: FETCH_BLOG, payload: data })
            }
        })
}

反应

const [fetchData, setFetchData] = useState(0);
    const showData = () => {
        setFetchData(fetchData + 10)
    }

    const dispatch = useDispatch();

    const { loading, error, blogs, } = useSelector(state => state.blogs)
  
    const getData = useCallback(  () => {
        dispatch(fetchBlogs(fetchData))
    }, [fetchData])

    useEffect(() => {
        getData()
    }, [getData])

在第一次渲染时,我获取了 10 个项目。单击加载更多后,我从数据库中获取了另外 10 个数据。在博客组件上它很好,但是返回主页并返回博客页面后;博客项目重复。如何解决此重复问题>

【问题讨论】:

    标签: reactjs react-redux fetch-api redux-thunk infinite-scroll


    【解决方案1】:

    这里有两个相互关联的问题,你可能不需要解决#2,这取决于你如何解决#1。

    1. 您应该向您的 thunk 操作添加一个条件,这样您就不会抓取您之前抓取过的页面。
    2. 您应该按页面分隔您的博客项目,这样如果您两次获取第 1 页,您就不会总是将最新的项目附加到数组的末尾。

    旁注:[...blogs.blogs] 是不必要的,因为有理由克隆您没有更改的属性。

    我对您的 API 调用感到困惑。看起来/blog?show=20 正在获取帖子 21-30,但我认为基于名称 show 它将是帖子 1-20。

    使用位置索引:

    import { createAsyncThunk, createReducer } from "@reduxjs/toolkit";
    
    export const fetchBlogs = createAsyncThunk(
      "blogs/fetchBlog",
      async (startIndex, { getState, rejectWithValue }) => {
        const res = await fetch("http://localhost:5000/blog?show=" + startIndex, {
          method: "GET",
          headers: {
            // where does userData come from ??
            authorization: userData.token
          }
        });
        const data = await res.json();
        if (data.message) {
          rejectWithValue(data.message);
        } else {
          return data;
        }
      },
      {
        condition: (startIndex, { getState }) => {
          const { blogs } = getState();
          // cancel if loading of if first post on paage is loaded
          if (blogs.loading || blogs.blogs[startIndex]) {
            return false;
          }
        }
      }
    );
    
    const initialState = {
      blogs: [],
      error: "",
      loading: false
    };
    
    export default createReducer(initialState, (builder) =>
      builder
        .addCase(fetchBlogs.pending, (state) => {
          state.loading = true;
          state.error = "";
        })
        .addCase(fetchBlogs.rejected, (state, action) => {
          state.loading = false;
          state.error = action.payload ?? action.error;
        })
        .addCase(fetchBlogs.fulfilled, (state, action) => {
          const startIndex = action.meta.arg;
          const newBlogs = action.payload;
          // insert in the array at the correct position
          state.blogs.splice(startIndex, newBlogs.length, newBlogs);
        })
    );
    

    使用分隔页:

    import { createAsyncThunk, createReducer, createSelector } from "@reduxjs/toolkit";
    
    export const fetchBlogs = createAsyncThunk(
      "blogs/fetchBlog",
      async (pageNumber, { getState, rejectWithValue }) => {
          const startIndex = 10 * (pageNumber - 1);
        const res = await fetch("http://localhost:5000/blog?show=" + startIndex, {
          method: "GET",
          headers: {
            // where does userData come from ??
            authorization: userData.token
          }
        });
        const data = await res.json();
        if (data.message) {
          rejectWithValue(data.message);
        } else {
          return data;
        }
      },
      {
        condition: (pageNumber, { getState }) => {
          const { blogs } = getState();
          // cancel if loading of if there is a property for this page
          if (blogs.loading || blogs.blogs[pageNumber]) {
            return false;
          }
        }
      }
    );
    
    const initialState = {
      //arrays keyed by page number
      blogs: {},
      error: "",
      loading: false
    };
    
    export default createReducer(initialState, (builder) =>
      builder
        .addCase(fetchBlogs.pending, (state) => {
          state.loading = true;
          state.error = "";
        })
        .addCase(fetchBlogs.rejected, (state, action) => {
          state.loading = false;
          state.error = action.payload ?? action.error;
        })
        .addCase(fetchBlogs.fulfilled, (state, action) => {
          const pageNumber = action.meta.arg;
          state.blogs[pageNumber] = action.payload;
        })
    );
    
    // want to flatten the blogs array when selecting
    // create a memoized selector
    export const selectBlogs = createSelector(
        state => state.blogs,
        (blogsState) => ({
            ...blogsState,
            blogs: Object.values(blogsState.blogs).flat(1)
        })
    )
    

    带组件:

    export default () => {
      const [pageNumber, setPageNumber] = useState(1);
    
      const showNext = () => {
        setPageNumber((page) => page + 1);
      };
    
      const dispatch = useDispatch();
    
      const { loading, error, blogs } = useSelector(selectBlogs);
    
      useEffect(() => {
        dispatch(fetchBlogs(pageNumber));
      }, [dispatch, pageNumber]);
    

    【讨论】:

      猜你喜欢
      • 2017-12-27
      • 2021-07-22
      • 2020-03-18
      • 1970-01-01
      • 2021-05-23
      • 2020-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多