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.
Related
My app has an insertion cursor that moves with scroll:
Its position / index often changes many times per second, and ought to do so smoothly. The cursorIndex state currently resides in my App component's state, i.e.:
function App(props) {
//...omitted...
const [cursorIndex, setCursorIndex] = useState(0)
//...
}
If I move it to store, will the overhead of dispatching actions (assuming that I prevent unnecessary re-renders) slow down the animation of it moving between multiple items per second? I'm not sure if this kind of highly temporary state is what Redux is really designed for. Though I'm new to Redux and I'm not sure.
I'm thinking of moving it to store because when an item is added, I need to increment the index so that it ends up below the item, not above it. Is there any way that a redux action can call a component's function or anything like that?
I suppose I could make a variable in store like 'needsCursorIncrement' and then put a useEffect hook on it that increments the index, but that feels ugly. Alternately, I could simply call setCursorIndex(currentIndex => currentIndex + 1) every time I dispatch addItem, but that also feels like bad design. The best thing to do might be to move it store, I just don't know if that's inviting disaster.
Appreciate all Redux wisdom and expertise.
Yes, Redux is not meant for that kind of high-frequency updating. In addition, a "cursor index" sounds like it's only needed by the component that's doing that rendering, and not the rest of the app.
Per the described use case, this may be one of the rare situations where you might want to update component state based on props / data read from the Redux store (ie, "we had 10 items, now we have 11, update the state accordingly". This is also the only real case where it's valid to queue a state update while rendering in a function component:
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#render-behavior-edge-cases
So, I would move that cursor index into the lowest possible component that cares about it (like a <DatesList /> or something), store it as component state, and update that state if necessary when relevant Redux store state changes (like the number of items).
For more details, see these sections of the Redux docs:
https://redux.js.org/tutorials/fundamentals/part-5-ui-react#global-state-component-state-and-forms
https://redux.js.org/style-guide/style-guide#evaluate-where-each-piece-of-state-should-live
https://redux.js.org/faq/organizing-state#do-i-have-to-put-all-my-state-into-redux-should-i-ever-use-reacts-setstate
I've been building quite an extensive app with React, Redux and Normalizr but have been struggling with a totally excessive number of redundant render cycles, and am now thinking I've perhaps misunderstood how to combine mapStateToProps and Normalizr.
In my mapStateToProps, I'm referencing all of a certain entity type, which I can then use to get an entity from its ID:
function mapStateToProps(state) {
return {
allMilestones: state.account.entities.milestones,
allTasks: state.account.entities.tasks,
}
}
Using the above mapStateToProps, whenever I need to get an entity, I can just go (for example) const taskObject = this.props.allTasks[taskId]. Quick and convenient, but I understand this means there's a lot of props being passed around - I thought passing around references wouldn't be a big deal, but then I noticed everything was re-rendering just, all the time.
Would this be a likely culprit for an unreasonable amount of render cycles?
If so, could I expect implementing Reselect for all of these cases would positively affect performance?
It's possible but this is a guesstimation without benchmarks (and you should try to clarify that yourself before making any optimisations). React-Redux will do a comparison of all of the keys of the object returned from mapStateToProps and will rerender if any are different.
If the reference changes each time - because you're using an immutable Map, for example - then the component will re-render any time mapStateToProps() is called.
You should try to make the return value more stable by only returning what is required by the connected component.
https://react-redux.js.org/using-react-redux/connect-mapstate#return-values-determine-if-your-component-re-renders
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.
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.
I have been using Redux for a few weeks now and I am very happy with it and I am getting used to a Redux way. I am using it with React. Still plenty to learn as both things are new to me.
I have a one problem - maybe I am doing something wrong ... Let me show you:
I have a component structure that looks like this:
App //root of the application aka smart component
CampaignTable
CampaignHeaderRow
CampaignHeader
CampaignDataRow
CampaignData
The App component is initialized as(only related code):
import * as DashboardActions from '../actions.js'
function select(state){
return {
campaigns: state.campaigns, // array of campaign objects, has name, id, time created etc
order: state.order // sort format "byWhichField"
// will affect the way how campaigns are displayed
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(DashboardActions, dispatch)
}
export default connect(select, mapDispatchToProps)(App);
App has now access to state and all actions as props.
The problem I see with it right now is:
I would like CampaignHeader to fire the action that will change the state.order state. Let say I will make <th>Text</th> inside CampaignHeader clickable. This will fire the action to change state.order which will in turn affect campaigns order on a next rerender.
So I have my action available inside App props. To pass it down to
CampaignHeader I would have to:
pass it down to CampaignHeader as props
assign it to variable inside CampaignHeader and pass it down as props to CampaignHeaderRow
assign it to variable inside CampaignHeaderRow and pass it down as props to CampaignHeader
assign it to variable inside CampaignHeader and fire the action inside onClick event....
This is a lot of boilerplate, assignments and bag passing! Just to get action fired.
All the components along the way are aware of this action.
When I decided to implement this feature I have opened CampaignHeader component file. I have added the logic and called the action, I have added the action to action file. All I needed is to get a props set. CampaignHeader component doesn't hold a reference to its parent so I didn't know straight away where should this props be injected from(in this example is obvious but I hope you get a point).
What if I will have even deeper component structure?
Is this approach correct?
Could I tackle this problem differently?
UPDATE:
As #Errorpro suggested will it be ok to connect single action and state.order to CampaignHeader?
Worried about: If I will do it once I will be doing it all the time.
There's a discussion in the issue-section of the Redux github repo about wether it's okay to use multiple connects or if everything should be passed down from the top through props, and in there Dan Abramov (the creator of Redux say's:
[...]
Nobody advocates a single connect.
[...]
The "single" only refers to small apps like the one we create in the
example. Please feel free to amend the docs to better clarify this. I
am now busy with other projects so please don't expect this issue to
get any movement unless somebody makes a PR. You can do it too.
The comment probably makes more sense in context though so check out the entire issue thread https://github.com/rackt/redux/issues/419#issuecomment-140782462
If you use redux you should know about dumb and smart component. So we use this sctructure:
component
index.js
Component.js
ComponentContainer.js
Dumb component just get props and render it. More interesting in smart component. Here it is:
export default compose(
relay({
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
any data from relay
}
`,
},
}),
connect(
null,
{
onCreate: createUserAction,
},
(stateProps, actionProps, parentProps) => ({
...parentProps,
onCreate={() => actionProps.onCreate('user')},
})
),
)(Component);
So, parentProps and onCreate function will be in dumb component's props. There you can use this.props.onCreate and invoke it or pass it farther.
Passing the actions - like any other props - from parent to child to grandchild etc is the idiomatic React way. In my opinion your approach is correct; even if it feels wrong.
But there are a couple of alternatives.
There is a feature in React called context. Context permits the passing of fields from a higher order component to a lower order component whilst skipping the middlemen. However, it's an experimental feature so I would recommend avoiding it for now. https://facebook.github.io/react/docs/context.html
Additionally, there is a Redux specific way where you can make any lower order node of your choosing a "smart component" (in the Redux sense). That is, you wrap your class export in the connect function to plug it directly to the store, in the exact same way you do for the Root node.
Personally I tend to stick to the top-down way. There may be a fair bit of boilerplate involved but at least it means your application is easy to reason about.