На самом деле, я реализовал функцию встроенного реагирующего плоского списка onLoadMore.он прекрасно работает без сагов на избыточность или immer js, значит, когда я реализовал его с использованием локального состояния, он работал нормально.Но когда я пытаюсь использовать его с immer или redux saga, он отображает экран вверху после каждого вызова извлечения.
UI Component
class Test extends React.PureComponent {
constructor(props){
super(props);
this._renderFooter = this._renderFooter.bind(this);
this._handleLoadMore = this._handleLoadMore.bind(this);
}
componentDidMount(){
const { from, to, onFetchRecipes} = this.props;
return onFetchRecipes({from, to});
}
_handleLoadMore(){
let { from, to, onFetchRecipes } = this.props;
from = to;
to = to + 10;
return onFetchRecipes({from, to});
}
_renderFooter(){
if (!this.props.recipes.loading) return null;
return (
<View
style={{
position: 'relative',
width: width,
height: height,
paddingVertical: 20,
borderTopWidth: 1,
marginTop: 10,
marginBottom: 10,
borderColor: theme.PRIMARY_COLOR
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render(){
const { recipes } = this.props;
const itemList = [{id: 1}, {id: 2}, {id: 3}, {id: 4}];
return (
<FlatList
style={{ marginBottom: 70, width: '100%'}}
data={ !recipes.loading ? recipes.data.hits : []}
renderItem={({ item }) =>
<CustomRow key={item.id} item={item}/>
}
keyExtractor={(item, index) => index.toString()}
onEndReached={this._handleLoadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={this._renderFooter}
initialNumToRender={10}
/>
);
}
}
const mapStateToProps = state =>({
to: makeSelectTo()(state),
from: makeSelectFrom()(state),
recipes: makeSelectRecipes()(state),
});
function mapDispatchToProps(dispatch) {
return {
onFetchRecipes: (params) => dispatch(fetchRecipes(params))
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'test', reducer });
const withSaga = injectSaga({ key: 'test', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(Test);
Actions
import * as actions from './constants';
export function fetchRecipes(params) {
return {
type: actions.FETCH_RECIPE,
params,
};
}
export function fetchRecipesSuccess(payload) {
return {
type: actions.FETCH_RECIPE_SUCCESS,
payload,
};
}
Selector
import { createSelector } from 'reselect';
import { initialState } from './reducer';
/**
* Direct selector to the test state domain
*/
const selectTestDomain = state => state.test || initialState;
/**
* Other specific selectors
*/
/**
* Default selector used by Test
*/
const makeSelectFrom = () =>
createSelector(selectTestDomain, substate => substate.from);
const makeSelectTo = () =>
createSelector(selectTestDomain, substate => substate.to);
const makeSelectRecipes = () =>
createSelector(selectTestDomain, substate => substate.recipes);
export { makeSelectRecipes, makeSelectFrom, makeSelectTo };
Редуктор
import produce from 'immer';
import * as actions from './constants';
export const initialState = {
to: 10,
from: 0,
recipes: {loading: false, data:{}}
};
const testReducer = (state = initialState, action) =>
produce(state, (draft) => {
switch (action.type) {
case actions.FETCH_RECIPE:
draft.recipes.loading = true;
draft.from = action.params.from;
draft.to = action.params.to;
break;
case actions.FETCH_RECIPE_SUCCESS:
draft.recipes.loading = false;
if(draft.recipes.data.hits && draft.recipes.data.hits.length > 0){
draft.recipes.data.hits.push(...action.payload.hits);
}else{
draft.recipes.data = action.payload;
}
break;
}
});
export default testReducer;
Сага
import * as actions from './actions';
import * as constants from './constants';
import Request from '../../utils/NetworkManager';
import {takeLatest, all, put} from 'redux-saga/effects';
function* workerFetchRecipes(action) {
try {
const params = action.params;
const response = yield Request.get('https://api.edamam.com/search', {params: {app_id: '4784637b', app_key: '12021eddf28af077a53a217fc81fc930', q:'chicken', from: params.from, to: params.to}});
yield put(actions.fetchRecipesSuccess(response))
} catch (error) {
}
}
function* watchAll() {
yield all([
takeLatest(constants.FETCH_RECIPE, workerFetchRecipes),
]);
}
export default watchAll;