Reputation: 1377
I am developing an app in react native and I need to stop/cancel redux saga when navigating between screens. How can I accomplish that?
I need this because I want to to avoid the lag when transitioning. You can see the video to get better my point https://streamable.com/45qsa
My code is below.
MindfulnessScreen.js
class MindFulness extends Component {
componentDidMount() {
this.props.dispatch(getMindFulness());
this.props.dispatch(toggleBottomBar(true));
}
render() {
const { isFetchingData, mindfulnessData, isLoggedIn, userType } = this.props;
const header = mindfulnessData.header;
const subHeader = mindfulnessData.subheader;
const imageBanner = FILES_URL + mindfulnessData.image_banner;
const mindFulnessDatas = mindfulnessData.children;
return (
<View style={{ flex: 1, backgroundColor: '#1F1F20' }}>
{isFetchingData && <LoadingIndicator />}
{/* <BottomBar screen={'MindFulness'} navigation={this.props.navigation} /> */}
<ScrollView style={{ flexGrow: 1, marginBottom: 35 }}>
{!isFetchingData && <FastImage
style={{
width: '100%',
height: 137,
display: "flex",
alignItems: "center",
}}
resizeMode={FastImage.resizeMode.cover}
source={{ uri: imageBanner }}
>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', paddingLeft: 30, paddingRight: 30 }}>
<Text style={{
textAlign: 'center',
fontSize: 20,
color: '#FFFFFF',
fontFamily: Theme.FONT_BOLD
}}>{header}</Text>
<Text style={{
textAlign: 'center',
fontSize: 14,
paddingTop: 8,
color: '#FFFFFF',
fontFamily: Theme.FONT_MEDIUM
}}>{subHeader}</Text>
</View>
</FastImage>}
{this.renderData(mindFulnessDatas)}
{!isFetchingData && isLoggedIn && userType == 0 && <View style={{
width: width,
height: 200,
marginBottom: 30,
borderRadius: 12,
shadowRadius: 16,
shadowOffset: { width: 0, height: 8 },
shadowColor: "black",
shadowOpacity: 0.47,
elevation: 2
}}
>
<FastImage style={{ width: '100%', height: '100%' }} source={unlockActivitiesBannerImage}>
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{
fontSize: 20,
color: '#FFFFFF',
textAlign: 'center',
position: 'absolute',
top: 40,
fontFamily: Theme.FONT_BOLD
}}>{'All Synesthesia Meditations \n 7 days for free'}</Text>
<CustomButton
disabled={false}
style={{
height: 50,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
marginTop: 45,
width: 230,
borderRadius: 45,
backgroundColor: '#25B999',
opacity: 1
}}
title="Start free trial"
onPress={() => {
this.props.dispatch(setMenuItem('7 days for free'))
this.props.navigation.navigate('Pricing')
}}
/>
</View>
</FastImage>
</View>}
</ScrollView>
</View>
)
}
}
function mapStateToProps(state) {
return {
isFetchingData: state.mindfulnessReducer.isFetchingData,
mindfulnessData: state.mindfulnessReducer.mindfulnessData
}
}
export default connect(mapStateToProps)(MindFulness);
AwarenessScreen is similar to MindfulnessScreen.js
MindFulnessAction.js
import { ActionTypes } from '../constants/constants'
export function getMindFulness() {
return {
type: ActionTypes.GET_MINDFULNESS,
payload: {}
}
}
mindulnessReducer.js
import { ActionTypes } from '../constants/constants'
const initialState = {
error: false,
isFetchingData: false,
mindfulnessData: [],
};
export const mindfulnessReducer = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.GET_MINDFULNESS:
return {
...state,
isFetchingData: true
}
case ActionTypes.GET_MINDFULNESS_SUCCESS:
return {
...state,
isFetchingData: false,
mindfulnessData: action.payload.node
}
case ActionTypes.GET_MINDFULNESS_FAIL:
return {
...state,
error: true,
isFetchingData: false
}
default:
return state
}
}
api.js
let commonHeaders = {
'Content-Type': 'application/json',
}
export const getMindFulness = (token) => fetch(`${baseUrl}node/337?token=${token}`, {
method: 'GET',
headers: {
...commonHeaders,
},
}).then(response => response.json());
mindFulnessSaga.js
import { AsyncStorage } from 'react-native';
import { put, call, select } from 'redux-saga/effects'
import { ActionTypes } from '../constants/constants'
import { getMindFulness, getMindFulnessAnonymous } from '../api/api'
export const getMindfulnessData = (state) => state.mindfulnessReducer.mindfulnessData;
const MindFulnessSaga = function* (action) {
const token = yield AsyncStorage.getItem('token');
const mindfulnessData = yield select(getMindfulnessData);
// if (mindfulnessData.length == 0) {
if (token !== null) {
const dataObject = yield call(getMindFulness, token);
if (dataObject.status.success) {
yield put({
type: ActionTypes.GET_MINDFULNESS_SUCCESS,
payload: {
...dataObject
}
})
}
else {
yield put({
type: ActionTypes.GET_MINDFULNESS_FAIL
})
}
}
else {
const dataObject = yield call(getMindFulnessAnonymous);
yield put({
type: ActionTypes.GET_MINDFULNESS_SUCCESS,
payload: {
...dataObject
}
})
}
// }
}
export default MindFulnessSaga
rootSaga.js
import { takeLatest } from 'redux-saga/effects'
import { ActionTypes } from '../constants/constants'
import MindFulnessSaga from './MindFulnessSaga'
import BeingAwareSaga from './BeingAwareSaga'
const rootSaga = function* () {
yield takeLatest(ActionTypes.GET_MINDFULNESS, MindFulnessSaga)
yield takeLatest(ActionTypes.GET_BEINGAWARE, BeingAwareSaga)
}
export default rootSaga
Any recommendations?
Thank you
Upvotes: 1
Views: 5265
Reputation: 3711
My original answer (I left it below) wasn't correct. I made a mistake while talking about the takeLatest
effect because, as the docs say
Spawns a saga on each action dispatched to the Store that matches a pattern. And automatically cancels any previous saga task started previously if it's still running.
So the previous MindFulnessSaga
/BeingAwareSaga
are automatically canceled by the takeLatest
effect. That doesn't mean that the AJAX calls are canceled too if you want to cancel the previous AJAX calls you need to manage the cancellation your own.
You can manage a saga cancellation by putting all its code in a try/finally and manage it in the finally
block
import { cancelled } from 'redux-saga/effects'
function* MindFulnessSaga() {
try {
// ... your code
} finally {
if (yield cancelled())
// the saga has been cancelled, cancel the AJAX request too
}
}
That said: the lag on navigation could be related to the fact that you don't cache the AJAX response (but you did it at the beginning since you have commented the // if (mindfulnessData.length == 0) {
block).
You need to:
takeLatest
with a custom infinite loop (docs)fork
(docs) the different sagas so you can cancel
(docs) them latercancel
the forked sagas when the user navigates to another screenLook at this answer of mine for a step-by-step explanation.
Let me know if you need more help 😉
Upvotes: 5