Store navigation state and resume from it only while registering - react-native

During the registration process of the app I want react-navigation to be able to resume from the component it was previously. However once the user is logged in, I no longer need to resume from where it was, I want it to start from the beginning.
I've got redux-persist for persisting my redux state and navigation state is connected correctly with react-navigation. However I am not sure if:
I can stop persisting navigation state once a condition is met. Seems that whitelist config is hardcoded in the persistor and can't be changed.
Is there a way to reset navigation state before StackNavigator gets it based on a condition store in the state itself.

Related

How do I pass a Redux action created by Redux Toolkit's createSlice to a screen in React Native

In my React Native app I have a generic settings screen that presents users with a question, and a list of options. The question and options are passed to the screen as navigation params (I am using react-navigation v5 and the useNavigation and useRoute hooks).
I want this settings screen to update my redux state when the user clicks on an option. However, the action to be dispatched should be dynamic based on the question being asked. I have a series of actions generated via Redux Toolkit's createSlice. I have exported these actions like so:
export const { changeColorScheme, changeDefaultUnits } = slice.actions;
I initially tried to pass these actions as navigation params to the settings screen, but got the "Non-serializable values were found in the navigation state" warning.
I have since tried to pass the action name as a navigation prop, and in the settings screen I tried dispatching the action with
const dispatch = useDispatch();
const route = useRoute();
[...]
dispatch({
type: route.params.actionName, // eg "changeColorScheme"
payload: theOptionTheUserSelected // eg "dark"
})
This latter method does not seem to trigger the action/reducer at all. I wonder if there's something to do with how Redux Toolkit creates and exports actions that means they cannot be dispatched in this manner?
Am I missing something? Is there another way to potentially achieve the same outcome?
Based on that warning, I assume that React-Navigation's "navigation state" is actually kept in the Redux store. You should not put non-serializable values like functions into the store state, so RTK warns about that.
For the dispatch issue, this is an excellent time to use the Redux DevTools to review what actions are being dispatched, including what the specific action type strings are.
Note that action types generated by createSlice are strings that have two parts, based on the slice name and the reducer name. So, instead of "changeColorScheme", it probably needs to be something like "settings/changeColorScheme". Be sure to use the value from the action creator's type field, like changeColorScheme.type, to have the right value here.

React native navigation how to connect it to Redux state?

I noticed below navigate is not updating the Redux state once it's passed to another component even the Redux data is updated.
navigation.navigate('anotherComponent', { firstname, lastname, loading, onRefresh });
In anotherComponent,
this.props.navigation.state.params
This works fine but does not update the Redux state value once it received the state from previous component. Updating Redux state won't change anything in anotherComponent.
How do I make this connect to Redux so anotherCompoenet actually updates data when Redux updated?
This can achieve when you connect anotherComponen with redux and save information in redux that you want in anotherComponent.

React Native: How to trigger remote fetch when switching back to tab based on state

I am building an app with 2 tabs (react-navigation). I am new to react and redux and I am still trying to wrap my head around how to communicate between components without creating too many unnecessary dependencies.
Tab A: The main component is fetching data via a remote API. For this I am using a react-redux and redux-thunk. The data is kept inside the central store since it is used for button states across different components (Tab A and Tab B). Pressing a button is calling a Thunk that deals with the asynchronous API call to update the server and then dispatches the action to update the store.
Tab B: Also fetches its data via a remote API but sets it via the component's State. I did not see the point of also putting this into the redux store since it is not shared across components. The button component from Tab A is also used here.
What I am trying to achieve: When the state inside the redux store changes (button's onPress() dispatches an action) both Tab A and Tab B require to re-fetch via the remote API but only under the following circumstances:
Switching from one tab to the other requires a re-fetch inside the target tab. Then, only when switching back to the first tab it should also trigger a re-fetch.
What I considered:
Adding a tabAinvalidated and tabBinvalidated flag to the redux store. I then listen to the willFocus event inside both tabs. I then re-fetch if the respective tab is flagged as invalidated. This might work but I am not sure if it is considered an anti-patter to keep update flags for individual components inside the redux store.
Q: Is there a better approach to this? What is the best way in react native to inform components that they need to reload their data from a remote API?
I think I came up with a solution myself. Instead of using the boolean flags for each tab (tabAinvalidated, tabBinvalidated) I am now just recording a timestamp for every update inside the redux store. For components which require re-fetching based on redux store updates I copy the redux timestamp into their own component's State each time I re-fetch the data. This way I can check if the component needs to re-fetch its own data from the server.
setFocus = async() => {
if (this.props.updateTs > this.state.updateTs) {
await this.loadData()
this.setState({ updateTs: this.props.updateTs, })
}
}
Seems to work fine.

React Native - FlatList - Internal State

I'm using a FlatList in order to implement a Store View in my react-native app.
I'm retrieving the store items from an API Call that is done in the componentDidMount cycle step of my component and then store it in my local state.
I've found in the Flatlist documentation this sentence :
Internal state is not preserved when content scrolls out of the render window. Make sure all your data is captured in the item data or external stores like Flux, Redux, or Relay.
I was wondering what it meant. I'm just using my local state and it seems to work just fine.
Is there any issue by doing so ? Could you provide me more informations about this particular point ?
Also, if you have any advice or optimization about my use-case, feel free to add them.
Thanks for your time.
Let's say you have a Contacts component with a FlatList rendering Contact component for each item in your data. Let's also say that these contacts are selectable. If you store these select value ( for example selected: true) in Contact components internal state and not in Contacts components state, when the item scrolls out its going to be unmounted and the state of that item going to be reset. If you hold it in the global data it will be created with the last state.
Hope I was able to explain.

Am i implementing redux correctly in my ReactNative app?

I am implementing a mobile application using ReactNative with Redux, the app that i am implementing looks like that:
Login (screen)
|--> Search for an object (screen)
|--> Show that object and edit it (screen)
|--> Take 2 photos (each photo a screen)
|--> A last screen for make a new object and save it
The above flow shows how each screen do their work and pass to the next screen.
My application state is the next:
{
auth: {
logged: false,
token: ''
},
somethingOfSideBar...
}
But i feel i am doing the things in the wrong way, because most of the screens have their own state, by example searchSomethingScreen fetch data from the server, check if it is valid and enable to pass to the next screen. I feel i am not doing the things in the Redux way, it suppose to make actions that change the entire state application, but i feel i do not need more state than i have. For me the global things are auth data and sidebar (because it is present across the entire application).
Should i make actions for every screen change?
Should i put more information in the global state application?
A one more thing, i have a AppContainer component which is used in connect to have access to the store, but i am passing parts of the state and the actions as well as children properties and i feel this is wrong as well.
I feel the Redux Reddit tutorial may be useful to you. It certainly was for me.
But i feel i am doing the things in the wrong way, because most of the screens have their own state, by example searchSomethingScreen fetch data from the server, check if it is valid and enable to pass to the next screen.
Used in the Redux way, API requests and their successful completion should each map to one action. Change the application state appropriately (in your reducing function) on each action and views/screens bound to your store will re-render. So if you're making an API request:
Create a Search container, mapping state searchResults to props and binding a Search component. (For example, see this container.)
Fire action REQUEST_SEARCH with search term on user input.
AJAX request is fired.
AJAX request successfully completes. Fire action RECEIVE_SEARCH with search results.
SearchReducer stores search results.
Bound Search component re-renders with search results.
Should i make actions for every screen change? Should i put more information in the global state application?
As a general rule, yes. But sometimes I've used component state (without dispatching an action) to store state that is local to the component (e.g. updating a text field).
A one more thing, i have a AppContainer component which is used in connect to have access to the store, but i am passing parts of the state and the actions as well as children properties and i feel this is wrong as well.
In general, higher level components should be containers, which inject state into the stateless components via props. You can have more than one containers and they work like components, so you can nest a container in another container. I encourage you to have a look at the docs, as it was very useful for me. :)