I'm using React Native and Redux to create an app that helps users find nearby fast-foods. I've got two components : Map and Settings.
Each component is connected (via react-redux) to its respective piece of state. Map can also dispatch an action creator called apiCall :
Map Component
...
connect(
({ map }) => ({ map }),
{ apiCall }
)(Map)
Settings Component
...
connect(
({ settings }) => ({ settings })
)(Settings)
I would like the action creator apiCall to read values from both map and settings pieces of state : this.props.apiCall(map, settings).
However, I want to avoid connecting my Map component to state.settings because it would re-render each time state.settings updates.
I'm still quite confused and have not found "the right approach" in solving this issue. These are the things I tried :
Connecting Map to state.settings and using shouldComponentUpdate() to prevent useless re-renders
Using getState() from the action creator to read state.settings value
Encapsulating everything in another higher component and then passing down specific props
The first two worked but seemed a bit anti-pattern and the third one was still triggering re-renders. Not quite sure why even though it felt like a good solution. I have not tried selectors yet but it seems to be another alternative.
To sum up, my question is :
How to dispatch an action that needs to read values from different pieces of state while avoiding unnecessary re-renders ?
You should use redux-thunk. That way you can return a function (a thunk) in your action creator, which will have access to state
const apiCall = (apiCallArgs) => {
return (dispatch, getState) => {
let mapState = getState('map');
let settingsState = getState('settings');
return dispatch(yourApiAction);
}
}
Related
I'm beginner, learning React with Redux, I came across a situation where,
These two code samples lead to the same result:
case 1: without using dispatch
export default connect(mapStateToProps,
{
getContacts : () => {
return {
type:GET_CONTACTS
}
}
}
)(Contacts);
case 2: using dispatch
export default connect(mapStateToProps,
dispatch => ({
getContacts : () => {
return dispatch({type:GET_CONTACTS})
}
})
)(Contacts);
Can someone explain me why does these two code examples work in the same way?
And why we don't need to use dispatch in case 1?
Both would result the same. There are two ways to define mapDispatchToProps.
Function form: Allows more customization, gains access to dispatch and optionally ownProps.
Object shorthand form: More declarative and easier to use.
Why to do dispatch instead of calling function normally?
In redux the store is single source of truth, the dispatch you are using is actually comes from store (store.dispatch).
If you call a function normally then it won't be aware by the store. That action won't pass through the middlewares (thunk/saga) that store is aware of and won't do perform store update via reducers.
If store is not updated, your connected components won't receive any updates. Eventually your UI won't re-render.
Now if i want to change value in store i should do following steps:
Go to constants/actionTypes file, create a line with action type
Go to actions and create action function
In each component where i use it i should create a function for mapDispatchToProps
In reducer i should write a logic of changing
Whats the point of such complexity?
Will it be wrong if i will do just one file with actions which will change the state? For example:
// actions.js
export const setCategories = (payload, setState, currentState) => setState({ categories: payload })
export const addCategory = (payload, setState, currentState) => setState({ categories: [...currentState.category, payload] })
To make it work i can create just couple of universal functions for all projects
1) getActions, which authomaticly collects all exports from actions.js and provide them to mapDispatchToProps, so in all components we could just write
const mapDispatchToProps = getActions
code of it can be something like
// actionsDispatcher.js
import * as actions from 'actions'
const getActions = (dispatch, ownProps) => {
Object.keys(actions).forEach(actionName => {
const fn = actions[actionName]
actions[actionName] = payload => dispatch({ action: fn, payload, type: _.toSnakeCase(actionName) })
}
return actions
}
which means we pass to dispatch the function of action from actions.js
2) setState which will work similary to react function, but for redux state
then in reducer function we just right
function rootReducer(state = initialState, action) {
if (action.action) {
action.action(action.payload, setState, state)
}
// here we make it extandable for ordinary way if required
if (action.type === '...') {
// ...
}
}
and nothing else...
So the question is whats wrong in such approach that will require for coder just write a function in one file 'actions.js' and call it from any component as props.someActionName(someParams) instead of changing 4 differents files?
Thank you
Redux is supposed to make complex requirements easier to implement but if you have simple requirements then it makes implementing these requirements more complicated.
The motivation mentions CQRS(Command Query Responsibility Segregation) that separates how you read from store (in redux with selectors and I'm a big fan of reselect) with how you write to it (with action and reducers).
The actions and reducers are the command (write) part of CQRS and is event sourcing, redux is sometimes referred to as an event store. This enables you to add or remove handlers (reducers or middle ware) for your events (actions) that can update the store, dispatch other events (=actions), do asynchronous stuff, write to local storage.
If you need to do all these things in one function (async fetch, write to local storage, call other functions (dispatch other actions),...) then that function becomes unmanageable.
Even if the function only calls other functions then it still needs to know the entire process of certain action. But if (for example) you had a local storage middleware that would write to storage on certain actions then no other code needs to know how or when it's called. So when logic of writing to local storage changes it is limited to the local storage middle ware.
This is the advantage of handlers (reducers, middleware) listening to events (actions), the handler only needs to know about a small portion of the process, not the entire process.
With event resourcing we also know why the state has a certain value instead of only knowing what the state is, the article states:
However there are times when we don't just want to see where we are, we also want to know how we got there.
Another big advantage of an event store is that you can re create the data by playing back the events. All this is excellently done with redux def tools.
Here is a great book on React with Redux.
Conventional-redux is a:
Library for small and medium react applications, it wraps the react-redux and provides API based on convention over configuration pattern without breaking redux compatibility.
You simply define an interactor:
class CounterInteractor {
// initial state
defaultState() {
return 0;
}
// actions:
doubleAsync() {
setTimeout(() => { this.dispatch('counter:double') }, 500)
}
// reduce methods:
onIncrement() {
return this.state + 1;
}
onDouble() {
return this.state * 2;
}
}
And dispatch actions to that interactor from your connected component component. That's it!
I don't really know why I can't get this to work. All the evidence talks against it...This is the situation:
I have a grid of data and a search panel. When the search panel is changed the searchparams are updated and used for updating the data grid.
The thing which triggers the chain is when the user changes the search panel. In my component i handle search panel changes with this:
getPhotos(key, value) {
const change = [{ key: key, value: value},{ key: 'page', value: 1}]
this.props.dispatch(updateSearchParams(change))
console.log('payload.searchParams', this.props.searchParams);
this.props.dispatch(
getPhotos(
{ context:this.props.params.context,
searchParams: this.props.searchParams }
)
);
}
Thus two dispatch calls to action creators form the component. The problem is that the searchparams are not updated in time for the getPhotos call, so the grid is not updated accordingly.
I thought that dispatch calls were synchronous - thus one after the other. I guess that it is the round trip from the component, to the action creator, to the store and reducer which is "screwing" it up.
The first call does not involve any asynchronous calls.
What is the "right" way of doing this? Please be specific about what goes in the component, the action creator and the reducer.
Thanks
dispatch is synchronous (unless you are using some middleware like redux-thunk). But after this.props.dispatch(updateSearchParams(change))
, your component needs to be updated (a re-render) or the this.props.searchParams is still the old one.
You can write this.props.dispatch(getPhotos(...)) in componentWillReceiveProps(nextProps), so you can access the new props (nextProps)
If you are using redux-thunk and two actions updateSearchParams and getPhotos are always bind together, you can create another aggregated action creator for them.
const updateSearchParams = change => dispatch => {
// return a promise here
// or use callback style etc. whatever you prefered
}
const updateSearchParamsAndGetPhotos = (change, context) => dispatch => {
dispatch(updateSearchParams(change))
.then(res => {
dispatch(getPhotos({
context,
searchParams: res.data.searchParams
}))
})
}
So now after dispatching a single action, your component should receive the new photos.
I had it wrong from the beginning.
The searchparams should not go into the store. I can handle the in the component alone - in the state of the component.
This the simplifies and eliminates the problem I described above.
Of cause there could be a situation where the searchparams needed to be available for other components. In that case I would go for #CodinCat answer above with the thunk. It works, i managed to implement it before my realisation.
Thanks
I would like to pass all the redux state into an easy to manage key on my props (I'm doing the same with the action creators). I would de-structure the state off the props that I actually need in the components that would consume them. Would the following create performance problems?
const mapStateToProps = (state, ownProps) => ({ reduxState: {...state}})
Inside my component I would destructure the state to consume like:
const {
geosuggestLocation,
mapLocationDetails,
userWants,
smartySuggestion,
geocodeAddress
} = this.props.reduxState
So far, everything works as expected, I would just like to know if it's a pattern I can continue to use without performance issues later down the road.
In my project I have action creator that depend on values that are in the state of the application to generate a new value or to decide what action to dispatch. My question is to know which is the right way to do it. I thought of two ways. Access those values within the action creator:
export const changePreviousPage = () => {
return (dispatch, getState) => {
let pagination = getState().appReducers.availability.pagination;
let previousPage = pagination.actualPage != 1 ? pagination.actualPage - 1 : pagination.actualPage;
dispatch({
type: types.CHANGE_PREVIOUS_PAGE,
previousPage
});
}
};
The other option I thought was to pass the value from the component to the action creator:
In my component
class Pagination extends Component {
...
handlePreviousPage() {
const {pagination} = this.props;
this.props.changePreviousPage(pagination);
}
...
}
In my action creator
export const changePreviousPage = pagination => {
let previousPage = pagination.actualPage != 1 ? pagination.actualPage - 1 : pagination.actualPage;
return{
type: types.CHANGE_PREVIOUS_PAGE,
previousPage
}
};
What is the best way to address it ?
In my opinion always use/retrieve the state at the closest time to execution, here the action creator (or rather more specifically the thunk you are returning that would then execute).
Remember that dispatch may have any number of middleware running before the actual store.dispatch call. This can include async middleware, so the state may have changed in between calling the dispatch and the store.dispatch call it will ultimately run.
Another one to consider is you may be dispatching multiple things in an action creator which change the state and invalidate what you passed into the action creator at the top. Also a reason why I consider let state = getState() at the top of an action creator a bad idea unless you are very sure nothing is going to change during your processing (as soon as you involve any API calls I would always use getState() again instead of using a stored variable).
Also putting data from state into props (using a redux container and connect helper method) will cause a rerender every time this changes, which could have a performance impact in some cases.
My personal coding preference is also to keep things as simple as possible in mapDispatchToProps (assuming that is where you're passing in your handlers like handlePreviousPage) and avoid any data processing (in your example it's not much, but you can easily see how that may get out of hand if you're preparing data for your action creator).