Access state property inside mapStateToProps - reactjs

I need to access specific properties of the redux state inside my containers. These properties will in turn be used as a key to fetch other nested properties and map them to component props. I use immutableJS.
Is the best way to do this inside the mapStateToProps? Will this cause any performance overhead or is it OK since the mapStateToProps already receives the whole state as a parameter?
Here is an example.
state selectors:
export const userSettingsState = state => state.get('userSettings');
export const transactionsState= state => state.get('transactions');
userSettings is an immutable Map and transactions is also a Map of userID and transaction list pairs.
const mapStateToProps = (state) => {
const curUID = userSettingsState(state).get('currentUserID');
return {
trList: transactionsState(state).getIn([curUID, 'trList'])
};
};
I need the currentUID to access the user transactions. The above sample works fine. But is it the best way?

Great question. Yes, this is the preferred way to handle computed props and to generate derived data. Not only is this the best way, but it's also very performant. Cory House (a very good React dev well known in the community) tweeted about something similar to this a few days ago and his same methodology applies to mapStateToProps(). Your only other option is to add a compute method to componentWillReceiveProps() AND componentDidMount(). This get's messy fast as you could imagine.
Now, some reservations about the way you're doing this.
Don't put async calls in here! This will also cause issues with rendering components as mapStateToProps() runs before the component mounts. If your component is waiting for promises before it mounts, you're going to take performance hits. This is probably obvious.
Speaking of performance, I am personally generating derived data exactly like this for a dropdown box with 8,600 entries that on user input, applies a fuzzy filter to those 8,600 entries, re-renders the dropdown list, and displays a new filtered list. There have been zero performance issues and the component is smooth as butter.

Related

What is the recommended approach for large react app with react-query?

I've recently started using react using functional components and react-query and it has been working fine except that I'm not clear about how to organize components properly.
the way I'm used to designing components is by having a top level component that performs all data access and passes data to it's child components through props. it also passes various callback handlers to child components so that when action is required, the top level component would update the data and passes the new data to child component. so in my case all calls to useQuery(), useMutation() reside in top level component but it's making the code extremely messy. but it's much like a page containing various child components that only display data or help user interact with data.
function Page(){
const [page, setPage] = useState(1)
const [size, setSize] = useState(10)
const persons = useQuery('persons', async ()=> await getPersons(page, size))
const addPerson = useMutation(async (args)=> {
const {id, name, desc} = args
await addPerson(id, name, description)
})
const person = useQuery('persons', async ()=> await getOnePerson(page, size), { enabled : false })
const addPersonCB = (id: number, name: string, desc: string)=> {
addPerson.mutate({id, name, desc})
}
// complex if/else logic to choose child components
the second approach is to disperse react useQuery() and useMutation throughout the components where it's need. and to further simplify things, if rendering logic is complex, each component would have a parent component that would perform the action and passes data as prop.
function PersonCard(props: PersonCardPropsType){
const {data, isLoading, isError, error} = useQuery(`personQuery${props.id}`, getPerson)
if(isLoading)
return <Wait />
if(isError)
return <Error reason={error} />
const record = data as PersonModel
return ( <PersonCardUI person={record} />)
}
and there are may compoenents for grid, form and etc each one in form of pair like
<PersonEditor />, <PersonEditorUI />, <PersonGrid />, <PersonGridUI />
in this case the calls are dispersed everywhere in the code. I want to know
For large projects, which approach is recommended and why?
Is the mix-match of Redux & react-Query okay? like for instance a grid has page size and page number which should go in redux, maybe?
Is it okay to use pure axios/fetch at some places with redux/react-query it's considered a frowned upon way of doing things?
It is generally considered a best practice to use useQuery where you need it. The separation into container / presentational components, while still possible, has been largely deprecated since hooks came around. With redux connect / mapStateToProps, it was best practice. Now, even in redux, you just call useSelector and useDispatch close to where you need it. This is no different in react-query.
There is a great talk on this subject from Mark Erikson: Hooks, HOCs and tradeoffs that I can totally recommend watching.
Using react-query hooks where they are needed not only avoids prop drilling, it also makes it easier for react-query to keep your data up-to-date, because more observers (=components that call useQuery) are mounting. This is also why it's best to just set a staleTime when you want to customize refetching behaviour. I've written about this in detail in React Query as a State Manager.
Is the mix-match of Redux & react-Query okay? like for instance a grid has page size and page number which should go in redux, maybe?
Totally, as long as you don't sync server state to redux. page number and page size are considered "client state" because the client is control over that state. The user selects the page, and the server responds with the data depending on it. I also like to abstract that away together in custom hooks:
const useData = () => {
const pageNumber = useSelector(state => state.pageNumber)
return useQuery(["data", pageNumber], () => fetchData(pageNumber))
}
that way, you have a hook you can use wherever you want (without passing anything to it), and it re-fetches data automatically if the pageNumber changes.
Is it okay to use pure axios/fetch at some places with redux/react-query it's considered a frowned upon way of doing things?
If you don't need caching / loading states managed for you etc then sure. The only thing that comes to my mind where I don't want a query / mutation might be file downloads or so :)
Let’s step back for a minute and see what each of these abstractions help us achieve & then it makes it easier to see how one should architect an application.
Very broadly, you have
useQuery useSwr
Redux or any other global state management tool
concept of lift state up
Context api
Saving state in url (filters say or link to say a product/item page)
useQuery useSwr are responsible for managing remote state and provide a snapshot of your data that resides behind a remote API. They help with fetching data, caching, error handling, showing loading spinners. They give us additional features such as refetch after a certain interval or refetch on focus, refetch a certain # of times on error etc. Whether we then decide to call these individually in each component or a parent component is a matter of design i.e. implementation detail.
Redux and other global state management tool help with managing local state, globally throughout your client application. A great example of that would be your auth’ed user. That information probably is required globally so redux sounds like a great place to have that information. Shopping cart is another example that might make sense in a redux store.
Lift state up when you want to share information with siblings. This stackoverflow question is a perfect example of lifting state up. DataTableComponent now becomes a controlled component or what you might call a presentation component.
If lifting state up becomes too cumbersome then look at context api or perhaps redux.
So, taking shopping cart as an example, you might decide that context api makes better sense or perhaps lifting state up makes more sense rather than having it in a redux store. My point being that there isn't one way of doing this and it will be a judgement call.
Lastly, you might have a page with filters say, and you may want to give your users an ability to send a link/Url & you might want the recipients to see the same information as the sender. So, now you must save state in your url via say query strings.
Going back to my comment above, there is no one way of doing things. So, you may start off by lifting state but then realize it's too cumbersome so you may switch to context api or even redux.
But each of these abstractions usually do have a place in your application & I have used all the above abstractions in conjunction with each other quite successfully.

Is it more performant to useSelector() on an object vs the object's properties?

Let's say we're working on our Customer Profile page with an object in the store called CustomerProfile.
export interface ICustomerProfileState {
status: string,
customerId: number,
contactInfo: IContactInfo,
financialInfo: IFinancialInfo
};
As we can see, this object consists of some plain properties as well as more complex ones. On the CustomerProfile.tsx page, let's compare two methods of tracking and updating the state of relevant properties. The first method is one where you useSelector on the individual state properties you want to track:
const status = useSelector((state) => state.customerProfile.status)
const preferredName = useSelector((state) => state.customerProfile.contactInfo.preferredName)
const altName1 = useSelector((state) => state.customerProfile.contactInfo.alternateName1);
const altName2 = useSelector((state) => state.customerProfile.contactInfo.alternateName2);
const preferredPayment = useSelector((state) => state.customerProfile.paymentInfo.preferredPaymentMethod;
Let's compare that with the second method -- simply tracking the object itself:
const customerProfile = useSelector((state) => state.customerProfile);
In the Redux documentation regarding hooks, specifically useSelector, it says:
When an action is dispatched, useSelector() will do a reference comparison of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, the component will not re-render.
This causes me to believe that the comparison above could amount to the same thing because the entire component will re-render whether one property changed or the entire object. But in our second method, where we useSelector(...state.customerProfile), I am making the assumption that we may needlessly re-render the component if irrelevant-to-the-component properties of customerProfile are updated elsewhere.
But maybe there is more happening under the hood with useSelector such that there is a performance difference between tracking individual properties on an object versus the entire object itself?
Performance is not a concern here. Either one will perform fine in any React application. Performance should be thought about after you have a working app and can do profiling. Identify the actual bottlenecks through profiling.
As for which style to use, use the one that is easiest to read and clearest to maintain. IMO a single selector with destructuring would be better. If profiling shows this to cause unnecessary re-rendering, then create separate selectors for each component of state and then one selector that uses those to put it back together in a single object so within the component there is still only one useSelector call.
Reusing and layering selectors with reselect is generally a best practice.

React/Redux : Events - state of the art

What is the state of the art in React to implement an event based pattern ( Publisher / Subscriber).
From one side we have components that listen to those events. Some of these components are visual, e.g. a chart that draws the result of a query other might not be visual, e.g. if we want to have components that manage queries and results.
From the other, any component can generate events.
Our first idea is adding in Redux the full list of events and their values and on each component implement in shouldComponentUpdate() a smart code that stops if the component does not listen to the changed events.
This doesn't look very elegant as all components listen to all events. Is there a more elegant solution (kind of state of the art) ?
Of course we want to be able to go back, forwars, save state and all this small details :-)
I think what you need to know is not so much "What is the state of the art" as "What is the canonical way" of doing publish and subscribe with React and Redux.
The short answer is that if you have a complex enough application, you should organize your store so that the application state is divided into slices and use the container pattern to separate the responsibility of publishing and subscribing. This way you avoid the problem you mentioned where you don't know what in the code base is changing the store and what is responding to the changes. You can look at a container component and see how it handles UI events from its children and produces a state change event. Or you can see how the container handles a state change and then drill-down the hierarchy. If you can make a Redux slice the responsibility of a single container, it's that much easier to think about events. All other components are not subscribed to the events per se, instead they receive the changes they need to render from props originating from the container component. And they notify the container component of their own events through callbacks passed down through props, so the container can publish them. This can go a long, long way, and if you feel that you need to pass down props too many levels, you can use React.children to flatten the nesting a bit, or in rare cases context.
The longer answer is a bit more difficult, since publish and subscribe is not super meaningful when talking about React. React should ultimately be responsible for rendering. As you mentioned, not all events are UI events. But if you model things such that all events that you can publish and subscribe to boil down to changing the store or reacting to a store change, then you can build your app focused more on Redux. Managing the queries and results you're talking about should be done in simple JavaScript modules without React. Then you can use a container component to glue the store to that module.
There are additional patterns people use like action creators and selectors. Those are good because at least the intent is to keep code bases familiar. But things are still moving a bit fast, with React moving towards the Hooks API and React-Redux trying to catch-up. But slices and container components aren't going anywhere, they are a natural way to separate concerns.
redux to store the events
reselect/connect to subscribe to them (https://github.com/reduxjs/reselect#connecting-a-selector-to-the-redux-store)
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
Now you have your subscribed event as a prop in the component, the component will re-render only if the prop changes. No need for shouldComponentUpdate because your component doesn't have to listen to the whole store, it will only get the part of the store you define in the selector.

Redux: mapStateToProps why second parameter ownProps

I have seen many times people are connecting redux to component and they are using second parameter in mapStateToProps.
function mapStateToProps(state, ownProps) {
return {
rowData: state.table.rows[0],
};
}
Is in it useless? If child component has passed props from parent why should we map/pass them again in mapStateToProps?
What is the advantage of this?
Greetings
The usage of second parameter in mapStateToProps depends purely on the application. Mostly you may not need it, but in certain scenarios where the selector depends on the props to filter out the result, it is useful to use the props value from mapStateToProps
A scenario where your might need to make use of props are
Say you have a redux state data called used and you only need to show users in a specific region that comes as a prop to the component. You can make use of this prop value in mapStateToProps and return the filtered results instead of returning the entire result and filtering in render which might be a little less performant
You can avoid unnecessary renders, via retaining some possibly small subset of the store data with the help of some props.
For example, you have a slideshow application and Redux stores all the slides, and your component is responsible for a single slide. A prop value contains the slide number. By extracting the store data only for that specific slide, you make it easier for React and React-Redux to avoid unnecessary rerenders. Maybe the store changed due to some other user having edited another slide, in your collaborative presentation editor - no reason for your slide to get rerendered.
https://react-redux.js.org/api/connect#ownprops
Besides, Redux is global state, while your component can work with data just needed for that component. If you have plans to ever reuse your component, OR to ever consider switching from Redux (or even React) to something else, or just want to follow good practice by uncoupling Model from View, then it makes sense to separate your component from what's essentially a singleton global variable as much as you can. By carving out only the data you need, and possibly shaping it to the needs of your component, you performed a global Model to local ViewModel mapping, and it's easy to take your component elsewhere, without concern for what the global Model was when you implemented it. By using eg. Recompose, you can take it further and make things you can just move around freely.
There may overwrite the previous field, so it gives you a choice.
eg: return Object.assign({},state,ownProps);
The second arguments normally comes into picture when you want to extract the some values from the props or assign value in the return of mapStateToProps using your values in props, then the second argument comes into picture. A classic usage would be writing selectors which uses values from store and the props passed to the component.

Pass the entire global state to 'mapStateToProps'

I would like to pass to the redux connect 'mapStateToProps' function the entire global state
The, inside my components i can just access the state which is now the global state.
Is there anything wrong with that?
I don't want to slice the state into many chunks and give it sliced to each component but just give the entire global state.
Are there any performance problems with that ? I understand that it re-renders the entire component but with react's diffing algorithm i imagine it should be fine.
Using a 'this.props.count' is not as suggestive as doing a 'this.props.todoSection.count. Just a readability thing.
I read that 'time-slicing api' in react 1.6 could potentially improve the various performance problems related to mantaining a global state.
https://auth0.com/blog/time-slice-suspense-react16/
Technically it's possible, but the better practice is to create mapToStateProps in each component which using global state. And map the only needed props in it.
And then what? whenever something change anywhere in the state, regardless if its related to your component or not your component would rerender?
why would you ever want something like that? (just curious)
It sounds like you don't want to map the entire global state, just the relevant reducer. So you certainly could map just that one store:
const mapStateToProps = ({ todoStore }) => ({ todoStore });
That'd be much less costly than mapping the whole store, but at scale it could start causing performance issues across your app if the store gets big enough (and/or the end-user's browser is crappy enough). At best it's an antipattern.
Using a this.props.count is not as suggestive as doing a this.props.todoStore.count. Just a readability thing.
You could always namespace while still mapping only the data you need:
const mapStateToProps = ({ todoStore: { count, foo } }) => ({ todoStore: { count, foo } });
That'd give you the shape you want while only connecting the state required. But comes with the slight developer cost of repeating the property names - and still requires defining what state to map - which maybe defeats the purpose.

Resources