How do I pass a Redux action created by Redux Toolkit's createSlice to a screen in React Native - 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.

Related

React native how to access data variable globally for all screen explain with simple program

I want to set data variable globally and I have to update from any page (some of us saying redux can anyone please explain with simple program ?)
See with redux , varibales stored globally are called state, and it sits at the root level. Suppose you create a state object in a reducer :
const INITIAL_STATE = {
phoneNum: '',
}
and when you dispatch actions from your component, you can actually change its value by like dispatch({type:'CHange phone',value:7973913});
And you can dispatch from any any component, and it will trigger the change of phoneNum and even you can access anywhere like suppose this.state.loginReducer.phoneNum
You can check this link react redux for detailed info which are portrayed beautifully.
Hope it help.s

Is it possible to manipulate and update store data directly in our class or functions without dispatching actions?

I had implemented redux in my react-native application, on button click I had dispatched method in mapDispatchToProps and called API in action method directly and updated the store in the reducer with API response and retrieved the data from the redux store in mapStateToProps. it works fine.
We can retrieve the data from the redux store in any of our class it works, But Can we update or delete store value in redux directly from our class without dispatching actions in mapDispatchToProps.
Is it possible to manipulate and update store data directly in our class or functions?
No. The only way you can cause state updates in a Redux app is to dispatch actions to the store.
Yes, it is possible. For that you have to import store in your class like this.
import store from ./Filename
//Subscribe your store to get state
store.subscribe(() => console.log(store.getState()))
//dispatch any action from store like following
store.dispatch({ type: 'INCREMENT' })
Given the pattern follows the reducer state remaining immutable dispatch is the layer intended to guarentee the state would only be replaced in that pattern. Remember the function always returns state if using that state to render changes I don't think going around it will give the intended results. There could also be asynchronous calls which may return stale data if dispatch isn't used. I would think the reducer is simply a function so yes it could probably be changed somehow but odd behavior may happen.
We can directly access and modify store object in the following way
import store from './store'
store.dispatch({ payload: JSON.parse(access), type: SET_AUTHENTICATION })

Can't pass property through navigation route

I'm trying to pass property through navigation route (Drawer Navigation) according to the basic schema like:
to push params
onPress={() => {
this.props.navigation.navigate('Screen2', {
user: 'USER_A'
});
to get parameters on Screen2
const { navigation } = this.props;
const userName = navigation.getParam('user', null);
But the problem that Screen2 also contains it's own navigation (Stack Navigation)...
const CollapsScr = createStackNavigator({
Collaps: { screen: Screen2 }
});
So when I removing Stack Navigation from Screen2 I can pass parameters without any problems, but I would like to keep this navigation and still be able to pass parameter through Drawer Navigation route. There is any solution for that?
You can do this.props.navigation.dangerouslyGetParent().getParam() to obtain the parent parameter you need.
You can also do the other way around.
Before navigating you can do this.props.navigation. getChildNavigation(°YourGrandChildKey°).setParams(). Where YourGrandChildKey is the one you defined on your Drawer
EDIT.
The first solution i gived, gives you to get the parameter for your specific problem. I don't know how you app will have to work.
On what you asked in the comment, it depends. I've always used redux to store data that i was 100% sure i would've needed all around my app, or data that can change overtime that changes 2 or more screens layouts.
The problem of the first solution is that the data your are taking is stored into another spot of the application, leaving the param user visible in the whole drawer.
I would suggest to create a redux base structure. Doing that, you know what data needs to be put on redux that is almost certain needed somewhere else or, at least, is reachable for future updates.
For your specific case, i would move put the user data on redux, as it's helpful giving you information about this session of the app.
As said, you have to choose what solution you think would work better on your app, if you need that variable only for that specific screen, use the second solution, so only that screens has that information, without filling parts of app with unnecessary data.
Hope this helps you!

Prevent loss of state when unmounting view

My app uses a react-navigation DrawerNavigator component to allow the user to navigate through various screens within the app.
My react-native-maps MapView component is nested inside a screen accessible via the DrawerNavigator.
The problem I am finding is that if you navigate to another page in the app, and then navigate back to the map page, the whole map has to reload and previous markers/map configuration is lost.
Is there a way that I can prevent the screen from unmounting when navigating away, or another way of stopping the whole map from resetting? I won't post code below because I believe the issue to be more theory based as opposed to fixing a code bug.
You need to persist the state when the component is unmounted. You need a state management library.
I know of two state management libraries.
RxJS is the recommended library for use with Angular. Even though it is not an developed by Angular, it is still installed by default if you use the Angular CLI to bootstrap a project. This library is incredibly powerful, especially with handling asynchronous data flows, and it fits in really well with the angular DI system. My understanding is that you create singleton services to manage particular parts of your global state. You could have many RxJS services for different parts of your app. Your components can then tap into these services and get state information from them. There are libraries which help you integrate RxJS with react components but I cannot attest to their value.
Redux is the canonical way to manage global and persisted state in React. It differs from RxJS in many ways. First, you have only one redux store in your whole app and it contains the entire global state. Second, Redux is modeled on Flux and setting up the various 'players' for the first time can be a very involved process (but once you get it it's easy). I highly recommend making use of the combineReducers function to simplify getting set up. Third, redux does not manage async data straight out of the box, you will need to reach for redux-thunkif you have async data flows.
Redux is still my go-to for global and persisted state in react because of how it integrates. There is a library called react-redux which integrates the two libraries really well. It provides you with a function called connect. The connect function accesses your global state and passes it into your components as a prop.
You wrap your entire app in a store provider line so
export default () => {
<Provider store={store}>
<App />
</Provider>
Then your individual components can access state using connect. connect accepts a function which extracts parts of your state for you. The function could look like this.
const mapStateToProps = state => {
return {
stateVariable: state.variable
}
Now you know your component will receive a prop called stateVariable which is the value of variable in your global store / state. So you can write your component to accept this prop
class Component extends React.Component {
render() {
var { stateVariable} = this.props;
return (
<View>
<Text>{stateVariable}</Text>
</View>
)
}
Then you call connect on your component with the mapStateToProps function and hey presto
const ConnectedComponent = connect(mapStateToProps)(Component)
export { ConnectedComponent as Component }
You see how this injects the props as if you had written
<Component stateVariable={state.variable} />
In this way it is a solution to prop-drilling
In addition, you can use redux-persist to persist state between sessions, not just mounting/unmounting components. This library accesses localStorage on web or asyncStorage on native.
When you call connect on a component is automatically passes in a prop called dispatch. Dispatch is a function which is used to dispatch actions which make edits to your local store. as I said the system requires some setting up - you must create constants, actions-creators, and reducers to manage these action dispatches. If you watch the first 8 videos of this course you will be well on your way https://egghead.io/courses/getting-started-with-redux
At this moment in time my recommendation is to use Redux with React.

How to compute data using elements from different branch of state tree in Redux React Native?

I'm new to React, React Native and Redux so I'm trying to wrap my head around alot of these news concepts for the past few days.
One problem I ran into right now is computing new data in Action Creator, before wrapping it around action object and passing into reducer, that requires a piece of data from other branch within the state tree. How would you normally go about solving this? Changing the structure of the global state tree or map this piece of data to the component requiring it?
Given the state tree:
{
ListView:{
dataSource : a ListView.DataSource type
}
SubmitForm:{
title : 'name of title',
text : 'description'
}
isFetchingData: true/false
}
And supposedly, each branch is handled by a different reducer, and each branch's data is passed into separate component as props.
Here's the scenario (I'm translating the React tutorial to React Native using Redux ):
Submit button is clicked in the SubmitForm
--> dispatch an action to notify store that data is being sent, then async grab and send {title,text} to API server.
Upon success ---> compute the dataSource returned from API server and pass the result dataSource to reducer (according to the tutorial). And by computing dataSource, I mean dataSource.cloneWithRows(....) (explained here), which requires the dataSource from ListView as seen above.
So my thought was the Form component should not have a prop called dataSource, as this is from another branch in the state tree. But without it, I'm not sure how to achieve the desired dataSource. Changing (merging ListView and SubmitForm in this case) the structure of the state tree would also be strange, as to my understanding about Redux pattern. So could someone help me figure this out? Thanks
Thanks guys. I think I found the best solution by using redux-thunk (well I was actually using redux-thunk to handle async action, but didnt read up the api well enough). Basically the thunk is also injected with getState, so basically calling getState() will gain me access to the global state tree and that should solve my problem.
const actionCreator = (args) => {
return (dispatch,getState) => {
// dispatch action to notify store that data is being sent
fetch(url)
.then((response) => response.json())
.then((resData) => {
// dispatch action to notify data received
// compute new data using resData and current dataSource accessed by getState().ListView.dataSource
})
}
}
I have thought this problem before and I think this may be a way.
For example.
You have actionA,reducerA and branchA of store.
You have actionB,reducerB and branchB of store.
Now you want to read branchA and branchB at the same time and change them.
Ok,let us define two actions.
One in actionA(sub-actionA), which to change the branchA.
Another in actionB(sub-actionB), which to change the branchB.
And then, define a total action(total-action),which will call sub-actionA and sub-actionB in order.
The last problem is "How to read branchB in the sub-actionA".
ok, we can use the method 'getState' of store.
We import the store into the actionA, and call
store.getState()
It will return the whole tree of store.
This is a common question, and conveniently has an answer over in the Redux FAQ.
Short version: combineReducers solves a simple use case of splitting reducer functionality by domain/slice-of-state. You've gone past that, and now need to either add custom additional top-level reducer logic to handle this action, or include the additional needed data in the action itself.