Navigation redux and react native based on API call - react-native

I am using redux and react native in my mobile application.
I want to redirect to another screen based on API response. I don't know how to do that.
I have used action, reducer and store in my application.
Can you please help me to find standard solution?
Thank you in advance.

One approach to handle user navigation, for instance registering a user is to first define three actions : register, registerSuccess, registerError.
The one responsible for handling navigation will be registerSuccess that will only return a type 'REGISTER_SUCCESS'.
Then you're reducer will look like this (i'm using immutable.js https://facebook.github.io/immutable-js/docs/#/)
const initialState = fromJS({
submitSuccess: false,
});
function reducer(state = initialState, action) {
case REGISTER_SUCCESS:
return state.update('submitSuccess', (v) => !v));
default:
return state;
Then in your container you can use componentWillReceiveProps to redirect your user
componentWillReceiveProps(nextProps) {
if (nextProps.submitSuccess) {
// redirect your user
}
}

See doc how to connect navigation to Redux -> https://reactnavigation.org/docs/guides/redux

Related

Firebase authentication and redux-saga synchronizing state

I have a design question on how to manage firebase auth & redux saga states with react-native-firebase.
Example use-case
Let's start from the scenario that I have an app that uses the idToken for a variety of use cases, some in the views using information from the claims, and some in redux actions to make api calls.
Using redux-saga, I would expect to implement these two cases like so:
// in selectors.js
const getIdToken = (state) => state.idTokenResult?.token
const getUserRole = (state) => state.idTokenResult?.claims.role
// in view.js
const role = useSelector(Selectors.getUserRole)
// in actions.js
const idToken = yield select(Selectors.getIdToken)
With this in mind I want to make sure the idTokenResult is available & up to date in my state. I can do this we a few actions and reducers, by calling a login method & then relying on the dispatched event onIdTokenChanged to update my state on login & tokenRefreshes. Something like the following:
// in actions.js
function* onLogin(email, password){
yield call([auth(), 'signInWithEmailAndPassword'], email, password)
}
// This action would be called by an eventChannel which emits on each onIdTokenChanged
function* onIdTokenChanged(user){
yield put({ type: "UPDATE_USER", user: user, })
if (user){
const idTokenResut = yield call([auth().currentUser, 'getIdTokenResult'])
yield put({ type: "UPDATE_ID_TOKEN_RESULT", idTokenResult: idTokenResult, })
}
}
// in reducers.js
const reducer = (state = {}, action) => {
switch (action.type) {
case 'UPDATE_USER':
return { ...state, user: action.user };
case 'UPDATE_ID_TOKEN_RESULT':
return { ...state, idTokenResult: action.idTokenResult }
}
}
Problem
Here is when we run into a problem. I recently learned that the onIdTokenChanged is dispatched lazily, only when the getIdTokenResult() method is invoked link. This means that with the code above we cannot expect our state to be accurate, because when we call yield select(Selectors.getIdToken) it doesn't check getIdTokenResult() and therefore the onIdTokenChanged event is never dispatched.
Potential solutions
How do we overcome this problem?
Set up a timer which periodically calls getIdTokenResult() before the token expires, to trigger the event.
Should work, but defeats the purpose of having an onIdTokenChanged event. Also this means it will refresh the token hourly, even if it isn't needed or being accessed
Somehow call getIdTokenResult() in the selector?
It's an async method so it seems like an anti-pattern here and I'm not even sure it's possible
Use the library directly to fetch user states with auth().currentUser, and forget redux-saga
We lose the nice rerender functionalities that redux's useSelector provides. By accessing the state directly we'll need to figure out another way to trigger rerenders on auth changes, which defeats the purpose of using redux-saga
Something I didn't consider/implemented incorrectly?
Your suggestions are welcome and thanks in advance for you help! :)

Why datas not update after navigate on react native?

I have a basic tab navigation HOME-UPLOAD-PROFILE. 'HOME' screen has values which fetch with an api from a different web site. After user log in navigation system direct user to this home page and "fetch" working well. But when i upload something using 'UPLOAD' screen and then return 'HOME' screen datas not updating. Whereas 'componentDidMount' should works everytime and bring datas every clicking 'HOME' screen I could not find any solution or answere
export default class Home extends Component {
state = {
data: []
};
async componentDidMount() {
//Burada kullanıcı yoksa ülkeleri getireceğiz
const rest = await fetch(
'https://www.birdpx.com/mobile/fotografgetir/' + this.state.page,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-type': 'application/json'
},
body: JSON.stringify(datam)
}
);
const gelenVeri = await rest.text();
let convertedVeri = JSON.parse(gelenVeri);
this.setState({
data: convertedVeri
});
}
}
You can check by logging if componentDidMount is triggered.
componentDidMount won't be triggered when you navigate between tabs in tabNavigator, except for the first time when tabNavigator is mounted.
You can choose one of the following 2 ways to implement what you need.
1. Use global redux state instead of component state. (Recommended)
You can implement redux to your project (if you aren't using). Use redux state in Home component instead of current component state.
On Upload screen, once uploading is done you can change the redux store by dispatching an action. This will instantly reflect on your Home component.
2. Fetch data on component focus event
If you want to catch the focus event for a specific tab component, you can use didFocus event listener of navigation lifecycle.
const didFocusSubscription = this.props.navigation.addListener(
'didFocus',
payload => {
console.debug('didFocus', payload);
}
);
// Remove the listener when you are done
didFocusSubscription.remove();
If you are using react-navigation, components do not unmount when you navigate from one screen to the next on a stack navigator.
You can listen to navigation lifecycle events in a component (https://reactnavigation.org/docs/4.x/navigation-prop/#addlistener---subscribe-to-updates-to-navigation-lifecycle)
or if you are using withNavigationFocus HOC for passing navigation prop to a component you can use the isFocused prop to know about the recent focus on you component.

Running api fetch on entering screen

I am creating an app in React Native (create react native) and I am attempting to fetch data from an API every time a user transitions to a screen.
For navigation, I am using the React Navigation library with a combination of Drawer and Stack Navigators.
Typically, I have seen fetch handled via the ComponentDidMount() lifecycle. However, when navigating to a screen in a stack navigator, the ComponentDidMount() lifecycle doesn't appear to trigger and the fetch doesn't run.
Ex. user is on post index, then navigates to add post screen, submits and is redirected to view screen (for that post), then clicks back to return to index. Returning to index does not trigger ComponentDidMount() and fetch isn't run again, so results are not updated.
Additionally, sometimes I need to access navigation params to alter a fetch request when navigation between screens.
I originally was attempting to determine screen transition (when navigation params were passed) via componentWillReceiveProps() method, however, this seemed a bit unreliable.
I did more reading and it sounds like I should be subscribing to listeners (via react navigation). I am having a hard time finding recent examples.
Current process (example)
On the desired screen I would subscribe to listener(s) in the componentDidMount() method:
async componentDidMount() {
this.subs = [this.props.navigation.addListener('willFocus', payload => this.setup(payload))];
}
Based of an example, it sounds like its good practice to remove all subs when unmounting the screen, so I also add:
componentWillUnmount() {
this.subs.forEach((sub) => {
sub.remove();
});
}
then I add a setup() callback method that calls whatever fetch methods I require:
setup = (payload) => {
this.getExampleDataFromApi();
};
Additionally, sometimes I need to access Navigation params that will be used in API queries.
I am setting these params via methods in other screens like so:
goToProfile = (id) => {
this.props.navigation.navigate('Profile', {
exampleParam: 'some value',
});
};
It seems like I cannot access the navigation prop via the provided getParam() method when within callback method from a navigation listener.
For example, this returns undefined.
setup = (payload) => {
console.log(navigation.getParam('exampleParam', []));
};
Instead I am having to do
componentDidFocus = (payload) => {
console.log(payload.action.params.exampleParam);
};
Question
I wanted to ask if my approach for handling fetch when navigating seems appropriate, and if not, what is a better way to tackle handling API requests when navigating between screens?
Thanks, I really appreciate the help!

React Navigation - Check on which screen the user is

I am using react-navigation in my project and I need to detect if the user is on the Dashboard/Graph/Posts page.
For example, if I am on Posts page, I need a param to write a conditional.
e.g. Make a request if I am only at Posts page
Is it possible to check on which screen the user is?
You can get it through navigation object, try it
this.props.navigation.state.routeName
You can try the following,
on componentDidMount
componentDidMount() {
this.subs = this.props.navigation.addListener("didFocus", payload => {
console.log("PAY_LOAD...", payload);
// Check the payload and your logic(you can get the current screen name like => payload.state.routeName)
});
}
on componentWillUnmount
componentWillUnmount() {
this.subs.remove();
}
Note: Don't forgot to remove listener on 'componentWillUnmount()'

Handling browser history with React-Redux

We are using React-Redux in your application. The problem is that we want to do undo and redo Redux state based on user navigation from browser buttons. Assume user is in page A and user browses couple of other pages and then he navigates to page A, for instance. Now If user presses back button in the browser, he'll go back to page A but here we want to have the previous instance of state which application had when user the page A.
Is there a centralized approach to solve this problem that doesn't need to handle the state manipulation manually.
What you are trying to achieve is a default behavior of React-Redux. If you are not trying to dispatch some actions, which manipulates specific component's state, when a route changes, it should persist its old state, without any additional functionality.
So my guess is that you are dispatching some actions when new route loads the component. How it could be dealt with this (e.g not to fetch resources from rest API once it existed, which finally caused to manipulate component) is here: https://github.com/reactjs/redux/blob/master/examples/async/src/actions/index.js#L35
const shouldFetchPosts = (state, reddit) => {
const posts = state.postsByReddit[reddit]
if (!posts) {
return true
}
if (posts.isFetching) {
return false
}
return posts.didInvalidate
}
export const fetchPostsIfNeeded = reddit => (dispatch, getState) => {
if (shouldFetchPosts(getState(), reddit)) {
return dispatch(fetchPosts(reddit))
}
}
So what this is doing is that it won't pass a new data into component once route changes if it already exists, so the old data/state stays there. You can abstract this functions more to make it easily reusable for all the other components.