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.
Related
Can You please help to decide which one of the architectures will be better
(fetching list from API with with react-native, react-redux, redux-thunk)
example
// component
componentDidMount() {
this.props.dispatch(fetchFunction());
}
// thunk action
fetchFunction () {
dispatch START_LOADING
return dispatch (
fetch()
dispatch SUCCESS_LOADING
);
}
OR
example
// component
componentDidMount() {
this.setState({'loading': true})
this.props.dispatch(fetchFunction()).finally(
this.setState({'loading': false})
);
}
//thunk action
fetchFunction () {
return dispatch (
fetch()
dispatch SUCCESS_LOADING
);
}
My idea is about storing "loading process" in local components state? What are bad and good sides of this approaches?
as i see it - example 2:
If loading takes longer time, i can leave component (it gets unmounted) and i will see warning - "changing state on unmounted component"
1 example:
Saves a lot of extra data that i do not need in redux store (also a lot of data i need to exclude from persist), and for example if i have a web store product component, i will have a lot of UI props stored in redux (for example list with 10 products, and each product has it's own buttons with states) :(
BUT i like it because all the logic stays in redux-thunk-actions, and i do not need to care in component how to continue coding after dispatch, whether it was a promise or wasn't example:
this.props.dispatch(fetchFunction()).then(() => {});
or just
this.props.dispatch(fetchFunction());
.. some code
So far I've made simple projects, and both ways worked fine.
Can you please give some advice which way to go for the bigger project?
If components outside of this component's tree need to know about the loading state, then keep the loading state in the store so that those outside components can query that state.
In a large app, whether a components need to know of a given piece of state is something can changes; starting out, Component X has local state because no other components need to know about local state X, but as the app grows, new components, thunks, etc are introduced that do need to know about local state X, and it has to be moved to the store. So to address this, one approach is to put all state in Redux from the beginning so you don't have to worry about unforeseen refactoring of local to global state.
Keeping everything in the redux store also makes debugging easier because you can see all the values in redux devtools rather than having to find individual components to see their local state.
I have two independent screen
Login Screen
DashBoard
Now on the login screen i store the requested API data inside the state (this.state.data) and that stored data I want to show on the dashboard screen for example the name or the age or the country or whatever. Now how do I sync both of these screens, I am using React Navigation 2.0
On success of a login request you need to change your current component to the Dashboard component, which -
Can be done in two ways
If you are replacing the component then you can call <Dashboard data={this.state.data} /> and fetch it in your Dashboard screen as this.props.data
If you are navigating to the component, you can do it via this.props.navigation.navigate('Dashboard', { data: this.state.data }); and then in your Dashboard component you can fetch it via this.props.navigation.state.params.data
Hope it helps :)
If you are doing a small project, you can do this via props, as explained by #Aseem Upadhyay
But note that this method becomes ineffective as your project grows. Imagine that you have multiple screens, distributed in a hierarchy, in which one daughter screen needs to pass data to another, which is on a different node. To do this via props, it would be necessary for the parent component to pass these values to both screens. This form is very difficult to manage.
The ideal way to do this is through redux. With it, you create a shared store of variables, so you can access them anywhere in the application. The following link demonstrates how to configure redux in your project.
https://blog.cloudboost.io/getting-started-with-react-native-and-redux-6cd4addeb29
It is recommended to use redux for variables that need to be shared. If you have only local component variables, then you do not need to use it.
I hope this can help you in your projects.
Hugs!
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.
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. :)
I'm using Relay with React Native and have a problem during login & logout.
After login or logout, Relay keeps the store from the previous user. To solve this I use Relay.Renderer and Relay.Environment. As in, in each Renderer I put singleton object of Environment.
The problem is that I previously did a mutation on object of Relay.Store, as in
Relay.Store.commitUpdate(new CreateProfile(), callback).
Now it doesn't work. I guess this is because Relay.Store doesn't know anything about server endpoints. But Relay.Environment does.
And now I'm using something like this this.props.relay.commitUpdate(new CreateProfile(), callback). It works pretty well when the parent component is wrapped as Relay.Container, so it has relay object in props.
But what should I do in components which are not Relay.Containers and don't have Relay object in props?
Relay.Store is a globally accessible singleton instance of Relay.Environment and Relay.Store.commitUpdate() updates data in that global environment. But since you're using your own instance of Relay.Environment, to update it you need to use this.props.relay.commitUpdate(), as you noted. This updates the environment the container was rendered with.
If need to make mutations from child components of containers, that are not wrapped in a Relay.Container, there are two ways to do that. You could simply pass the relay prop to them, so in the render function of your container you would have:
<Child relay={this.props.relay} />
However, since those plain components are not in a Relay container, they don't currently need to know anything about Relay. If you want to keep them that way, you could write the method that does the update in your container component like this:
onCreateProfile = () => {
this.props.relay.commitUpdate(new CreateProfile());
};
and only pass a callback to your child component in render:
<Child onCreateProfile={this.onCreateProfile} />
If you need to make a mutation from a component that does not have a Relay.Container above it in the component hierarchy at all, you could create the Relay.Environment in a shared root component higher up and pass it down using props (or pass a callback using the strategy shown above).