React-navigation how to navigate to a route within redux - react-native

I am new to react native with redux. I'm using react native + redux (with redux-thunk) + react-navigation.
I perform an operation within my redux action and intend to call a react-navigation route.
export const storeClient = (client) => {
return async dispatch => {
....
//does not work within action redux
//this.props.navigation.navigate('ListCliente');
}
}
There is information there in the react-navigation documentation:
Warning: in the next major version of React Navigation, to be released in Fall 2018, we will no longer provide any information about how to integrate with Redux and it may cease to work.
does not react-navigation offer a way to solve this problem? Can someone tell me this? How can I direct the route within the action using the react-navigation
EDIT
Is it a good practice to pass navigation as a parameter to use within redux?
my component
this.props.storeCliente(this.props.navigation, client)
my action (redux)
export const storeClient = (navigation, client) => {
return async dispatch => {
....
navigation.navigate('ListCliente');
}
}

** UPDATED
You need to :
1- import NavigationActions into your actions file (i.e. actions.js)
import { NavigationActions } from 'react-navigation'
2- Replace every "navigation" with NavigationActions, and your piece of code that calls the navigation, should looks like:
const loginUserSuccess = (dispatch, user, navigation) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user,
});
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'employeeList' })],
}),
};

Related

React Native component not making new query after mount

We're using react-native-web so native and web are in one code base. I have an instance where a user clicks the back button to return to a main page and this should fire a re-query of the backend. We're also using Apollo hooks for queries, useQuery
So far, this works for web but not for native. I tried creating a useEffect hook to check if navigation and specifically navigation.isFocused() like so:
const {
data,
loading: childProfilesLoading,
error: childProfilesError,
refetch: refetchChildProfiles,
} = useQuery(LIST_PROFILES, {
fetchPolicy: 'no-cache',
})
// this method also exists on the previous page
const goBack = () => {
if (history) {
history.goBack()
} else if (navigation) {
navigation.goBack()
}
}
useEffect(() => {
if (navigation?.isFocused()) {
refetchChildProfiles()
}
}, [navigation, refetchChildProfiles])
but this doesn't work. Is there something I'm missing in forcing a refetch on native?

Is it possible to navigate inside a Redux toolkit action to another screen after an asyncthunk is fullfilled?

So recently I am working on a react native app. I did wonder how I navigate from a fullfiled action?
I did not find away to be able to do that. What i have so far is this:
Dispatch Action
Navigate without knowing the result.
How is this achievable.
Some code:
Dispatch in the Submit function:
dispatch(
createTopic({
title: values.title,
subject: values.subject,
subsubject: values.subSubject,
description: values.description,
uid: userUid
})
, [dispatch])
export const createTopic = createAsyncThunk('topic/createTopic',
async ({title, subject, subsubject, description, uid}) =>{
try {
console.log(uid)
const response = await firebase.firestore().collection("Users").doc(uid).collection("Topics")
.doc()
.set({
title: title,
subject: subject,
subsubject: subsubject,
description: description
})
return response
} catch (error) {
console.log(error)
}
}
)
I'm assuming since this is react-native that you are using react-navigation? It is possible to navigate within the Redux action itself using the methods described in the docs page Navigating without the navigation prop. But it's probably better to initiate the navigation from the component.
The result of dispatching a thunk action is a Promise. Redux toolkit wraps this Promise so that there are no uncaught errors in your component. It always resolves to either a success or failure action. But you can unwrap() the result to use it with a try/catch.
const dispatch = useDispatch();
const navigation = useNavigation();
const handleSubmit = async (e) => {
try {
// wait for the action to complete successfully
const response = await dispatch(createTopic(args)).unwrap();
// then navigate
navigation.navigate(...);
} catch (error) {
// do something
}
}
Note: you should not use a try/catch in your createAsyncThunk. You want errors to be thrown so that they dispatch a 'topic/createTopic/rejected' action.
If you want to navigate from redux action and any were event when you don't have navigation prop, use ref of the navigation container,
eg:
export const navigationRef = createRef()
// when adding NavigationContainer add this ref
ie:
<Navigationcontainer ref={navigationRef}>
{...rest code}
</Navigationcontainer>
now you can use that ref to navigate to other screens
eg:
navigationRef.current?.navigate('ScreenName', params)
use above line the redux action...

Dispatching Redux Data and Get the State

I stuck on Redux Implementation during developing an app using React Native and Redux. I do this for the first time and followed this example.
I've already installed Redux and React Native Navigation. I would like to save the state containing data for countries (the user picked a country and would like to keep the choice by the time when it browses to all screens).
Good. I've created a component that could be seen to all screens like this:
LinksScreen.navigationOptions = {
headerTitle: 'Links',
headerRight: <CountriesPickButton/>,
};
Next, I visualize the button and wait for a change in the component. By default, it should show primary country. Next, the user clicks on the button and it opens a modal where has a dropdown menu. For example, I show you the default fetching a country:
import { connect } from 'react-redux'
import store from '../../redux/countries'
export default class CountriesPick extends Component {
render() {.... // here is the button and modal, etc. It's work.
}
constructor(props, context) {
super(props, context);
this.state = store.getState();
store.subscribe(() => {
this.setState(store.getState());
});
this.defaultCountry(251);
}
async defaultCountry(countryId) {
return fetch(URL)
.then((response) => response.json())
.then((responseJson) => {
for (const key of Object.keys(responseJson.result)) {
// this works for current screen: this.setState({ defaultCountry: responseJson.result[key], selectedCountry: responseJson.result[key].country_id });
store.dispatch({ defaultCountry: responseJson.result[key], selectedCountry: responseJson.result[key].country_id , type: 'countries' });
}
return responseJson.result;
})
.catch((error) => {
console.error(error);
});
state = {
showModal: false,
countries: [],
selectedCountry: 0,
defaultCountry: [],
type: 'countries'
};
}
Without store.dispatch({}) I can change the state with the country but it has not to share between screens. That's because I started with Redux.
Here is the Redux code ():
import { createStore } from 'redux'
const defaultState = {
showModal: false,
countries: [],
selectedCountry: 0,
defaultCountry: [],
type: 'countries'
};
function store(state = defaultState) {
return state;
}
export default createStore(store);
Something is not like it should be. When I invoke store.dispatch({...}) it's not changing the state, it returns the default array. I guess I should use <Provider></Provider> in App.js to catch every change but first, I need to understand what I wrong?
Is it connected at all? In the example that I followed, I did not see connect(). Also, I'm not sure I'm using type properly.
Thank you in advance.
Problems here are the following:
Example on the link you provided is bad to say the least. Do not follow it
You said to be using react-native-navigation, but the code you provided comes from react-navigation. I suggest using the latter, especially for starters
Your createStore code is not going to work, as reducer for the store should be a function of state and action
With that being said, you should definitely see Basic Tutorial of redux with examples. You will almost never have to do store.getState() or store.dispatch() while using react with redux, as react-reduxpackage (included in the tutorial I linked) will do this for you. You will instead declare dependency between your store state and props your component receives

Issue with Unstated and React Navigation in React Native

I have the function
onPress = (store) => {
//store.flipState();
this.props.navigation.navigate('anotherScreen');
console.log('hi');
}
If I run it as above the navigation works.
If I uncomment the store.flipState() line the state changes but the navigation doesn't work (the screen just refreshes).
The console.log works in both cases.
How can I change the state and navigate at the same time?
I use Unstated and React Navigation in React Native.
Thank you.
I know this is really old, but what if you pass the navigate action to flipState
const {navigation: {navigate}} = this.props
store.flipState(navigate('anotherScreen'))
then, in flipState, when you call setState, pass the navigate as the success action callback
flipState = (callback) => {
this.setState((state) => {
return { flippedState: !state.flippedState };
}, callback);
};

React-Navigation: navigate from actions file

I'm new to RN and JS.
I want to navigate once a login action is complete, but I cannot get it to work. I am using firebase.
This is from my actions file. It throws a firebase error:
export const LOGIN_USER_SUCCESS = 'login_user_success';
export const loginUserSuccess = (dispatch, user) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
});
this.props.navigation.navigate('groupMain'); // this doesnt work
};
I also tried putting it into the Login component - this might be a step in right direction because there is no firebase error, but nothing happens. So it seems that all works, except the user is not navigated to the proper screen. (I removed the 'this doesnt work' line from above)
componentWillReceiveProps(nextProps) {
this.onAuthComplete(nextProps);
}
onAuthComplete(props) {
if (props.user) {
this.props.navigation.navigate('groupMain');
}
}
Later, I also found this in the official docs and tried to implement by using this code in my action, but it threw a firebase error:
"dispatch - Send an action to the router
Use dispatch to send any navigation action to the router. The other navigation functions use dispatch behind the scenes.
Note that if you want to dispatch react-navigation actions you should use the action creators provided in this library.
See Navigation Actions Docs for a full list of available actions.
import { NavigationActions } from 'react-navigation'
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
// navigate can have a nested navigate action that will be run inside the child router
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)"
thanks!
I solved a similar problem by creating a global service to handle programmatic navigation:
services/navigator.js
import { NavigationActions } from 'react-navigation';
let navigator;
export function setNavigator(nav) {
navigator = nav;
}
export function navigate(routeName, params) {
if (navigator) {
navigator.dispatch(NavigationActions.navigate({routeName, params}));
}
}
export function goBack() { ... }
export function reset() { ... }
Then, in my top-level component I store the reference to the navigator when it's created:
screens/App.js
import { setNavigator } from '../services/navigator';
const AppNavigator = StackNavigator(...);
class App extends Component {
render() {
return (
<AppNavigator ref={nav => setNavigator(nav)} />
);
}
}
And finally in my action files (or wherever I need to), I simply use the service to dispatch navigation actions:
actions/login.js
import { navigate } from '../services/navigator';
export function loginUserSuccess() {
// use navigate() anywhere you'd normally use this.props.navigation.navigate()
navigate('NextScreen');
}