Which approach to storing loading processes is preferable? - react-native

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.

Related

Global vuex watchers

I have a Vuex store feeding many Vue components in a somewhat large web app. I would like to be able to watch Vuex states or getters for logging purposes. However, I want to be able to watch them 'globally', i.e. without relying on any particular Vue component. This rules out tricks like
//aComponent.vue
watch:{ '$store.state.data'(value, oldValue) { /*do something here*/ }}
and
//App.vue
created() {
this.$store.watch((state) => state.watchedState) => {/*do something here*/ })
}
Instead, I am looking for a way to watch my Vuex states or getters directly from the module where the store is defined, so that whenever a state changes, I can call a function passing it as arguments the old state and the new state, so as to write the change into a database (ideally the change itself would be detected automatically, as in Vue's watch(new,old) {...}.
I suppose I should use Vuex native method store.watch() for this purpose. But so far I have not be able to invoke it from the store module, as desired.
Any clue? Or did I miss something and what I am trying to do would be just as efficiently done with a different technique?
For watching mutations you have this options.
You can subscribe to a store and get notified by mutation after it happens.
store.subscribe( (mutation, state) => {
})
You can achieve this in a couple of ways:
use subscribe on the store you create by hand, most likely in main.js
create a plugin that gets appended to each instance of a store and again subscribe to it
just use the vuex official plugin for logging
For watching getters you probably need to monkey patch the getters property by hand.
Then again, if you want to watch what happens with vuex during development you can use vuejs tools

Run method on component with Vuex

I have a Vue component that contains a Leaflet map. For the time being I'm using the event bus pattern to run some Leaflet methods when other components request it:
// in Map.vue
eventBus.$on('invalidateMapSize', () => {
this.map.invalidateSize(true)
})
eventBus.$on('setMapView', (coordinates, zoom) => {
this.map.setView(coordinates, zoom)
})
// in other components
eventBus.$emit('setMapView', [47.6623, 23.6970], 15)
Now I want to transition my app to Vuex and my question is how to achieve the same functionality?
While it is true that there is a trend on using VueX and steering away from the event bus pattern, I personally find both patterns to be complementary.
VueX states match well with Vue component data/props and their derivatives (computed, watch).
But there are cases where you do not manage a state, but really some event: a user click that should trigger a one time action (and you do not care about recording such a change, so a state is useless / overkill for this purpose); a network response that carries temporary data (like a search result); etc.
For these cases, I still use an event bus, since the equivalent feature has been purposefully removed from VueX.
In your situation, it seems to me that it fits well in the latter case: Leaflet does not (only) display static data derived from some states (could be the case of Markers, Tile Layers...), but enables user interactivity where your app can interfere by forcing a view from time to time, based on some events.
Trying to achieve this functionality using states would very probably require recording the last requested map view in a state, watching that state, and triggering a side effect on state change. Then there is an issue when it is requested to re-set the view to the same position...

Vue - Passing data between dynamic components using eventBus?

I have 3 Components in my 'Search' (parent) View; 'SearchForm', 'ResultList', 'DetailPage'. I switch from the SearchForm to the ResultList when I have received a response from the backend.
<keep-alive>
<component v-bind:is="currentComponent"></component>
</keep-alive>
When a response is recieved in my 'SearchForm' I save it to the searchBus;
searchBus.$emit('searchIssue', response.data);
Then, in my ResultList I want to retrieve it again and display the results;
mounted() {
searchBus.$on(['searchIssue'], (search) => {
this.table_items = search;
});
}
I display a loading animation (also a component) until the response is fully loaded and the ResultList is displayed.
Due to the Vue lifecycle everything is working when all components are displayed in one View, as they are already listening when the bus is updated.
Should I choose a different approach? E.g. using v-show or pass the response back to the Parent and inserting it again with a prop (Idk if it would work as not all components have the same props).
Is there a way to use the Bus anyway ? And how could it be solved making it one linear hierarchy and still hide the non-relevant components? (SearchForm -> ResultList -> DetailPage)
Should I choose a different approach?
I thing that is coming time for using Vuex
At the center of every Vuex application is the store. A "store" is
basically a container that holds your application state. There are two
things that make a Vuex store different from a plain global object:
Vuex stores are reactive. When Vue components retrieve state from it, they will reactively and efficiently update if the store's state
changes.
You cannot directly mutate the store's state. The only way to change a store's state is by explicitly committing mutations. This
ensures every state change leaves a track-able record, and enables
tooling that helps us better understand our applications.

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.