如何在 Flatlist 上使用无限滚动的 redux saga?

How to use redux saga with infinite scroll on Flatlist?

我正在开发我的第一个 React Native 应用程序,这是我第一次使用 redux 和 redux saga。所以我建立了一个 Flatlist 来无限滚动,API 端点是 return 的帖子(每页 10 个)。但我不知道如何使用 reducer 来 return 帖子,控制加载指示器并跟踪商店中的页码,使用 redux saga。

我的代码如下:

Home.js

this.state = {
    page: 1,
    totalPages: 10,
    loading: false,
}

componentDidMount() {
    this.loadMorePosts();
}

loadMorePosts = () => {
    this.setState(() => { loading: true });
    this.setState(() => { page: this.state.page++ });
    this.props.loadPosts(this.state.page);
}

<AnimatedFlatList
    ...
    onEndReached={this.loadMorePosts}
    onEndReachedThreshold={0.2}
/>

const mapStateToProps = state => ({
    posts: state.posts,
});

帖子操作

export function loadPosts(page){
    return {
        type: Types.FETCH_POSTS,
        payload: { page }
    };
}

帖子传奇

export function* fetchPosts(action) {
    const response = yield call(api.get, `/posts/${action.payload.page}`);
    yield put({ type: Types.LOAD_POSTS, payload: { posts: response.data } });
}

帖子减速器

export default function posts(state = initialState, action) {
    switch(action.type) {
        case Types.LOAD_POSTS:
            return [ ...state, ...action.payload.posts ];
        default:
            return state;
    }
}

有了这个我可以获取帖子并加载到 Flatlist 中,但是如果我更改屏幕我会忘记实际页码,它将在 Home.js 构造函数中再次设置为 0。并且没有视觉反馈,因为加载状态没有用 mapStateToProps 函数定义...

谁能帮我解决这个问题?

扩展评论:(未经测试的代码但原则在那里)。

传奇

export function* fetchPosts(action) {
    try {
        yield put({ type: Types.LOAD_POSTS_START, payload: { page: action.payload.page } });
        const response = yield call(api.get, `/posts/${action.payload.page}`);
        yield put({ type: Types.LOAD_POSTS, payload: { posts: response.data } });
    }
    catch {
        //perhaps roll back page count?
        yield put({ type: Types.LOAD_POSTS_END, payload: { } });
    }
}

减速器

const initialState = {
   isLoading: false,
   currentPage: 0,
   posts: []
}
export default function posts(state = initialState, action) {
    switch(action.type) {
        case Types.LOAD_POSTS_START: 
            return {
                ...state, 
                currentPage: action.payload.page,
                isLoading: true
            };
        case Types.LOAD_POSTS_END: 
            return {
                ...state, 
                isLoading: false
            };
        case Types.LOAD_POSTS:
            return {
              ...state, 
              isLoading: false,
              posts: [ ...state.posts, ...action.payload.posts ]
            };
        default:
            return state;
    }
}

然后在您的组件中连接到此状态,而不是将其存储在组件状态对象中

做一个 saga/task 只做一个 fetch 和 returns 这样的承诺:

const fetchAction = (input, init) => {
  let resolve;
  const promise = new Promise(resolveArg => resolve = resolveArg)
  return { type:'FETCH_ACTION', input, init, promise, resolve }
}

function fetchActionWorker({ input, init, resolve}) {
   const res = yield call(fetch, input, init);
   resolve(res);
}

function* fetchActionWatcher() {
   yield takeEvery('FETCH_ACTION', fetchWorker);
}

然后像这样使用它:

class List extends Component {
    render() {
       return <Button title="fetch" onPress={this.doFetch} />
    }
    doFetch = async () => {
        const res = await dispatch(fetchAction('url', { method:'GET' })).promise;
    }
}

立即调用 fetch 操作即可获得承诺。