It is a conceptual question about a better way of fetching data using react and react with redux.
I have a component called UsersSelect which is a select component that displays the user information. Currently I'm fetching the user data inside this component to make it more reusable.
I also have an UsersTable component which does kind of the same as UsersSelect and, sometimes, I have them both rendered on the same screen which causes two identical fetches. To avoid this, I'm using redux-saga with takeLatest.
So my questions are:
Is it ok to use this?
Should I put the fetch logic on the parent component? Like an UsersPage component?
Is there another better option to simplify this?
I agree with Miro J.'s comment.
Since you're using the same data into two different components, it'd be better to fetch the data in the parent and pass it down as props to the children.
In your case, (it seems) both your components are very closely related.
In cases in which the components are not so close, but you'd still need the data, you could also load the data when setting up the root App component.
For react architecture, without redux , fetch data is working in parent, and yes UsersPage component should has data for the child components for work, but with redux you has some options, like to create a presentational components , or implement a factory pattern but this requiere that backend architecture couple
const factoryComponent = (FactoryComponent, DefaultComponent, props) => ({
Component: FactoryComponent || props.component || DefaultComponent,
props: { ...props, component: FactoryComponent ? props.component : fetch(url).then(data=>console.log(data)); },
})
Related
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.
I'm using the redux store for all my major components, but for smaller components, I've just been passing down regular react props. Which is more efficient?
If have many smaller components, does it make any difference to pull from redux or pass down in regular props?
The Redux docs says:
many individual components should be connected to the store instead of just a few
This sounds like I should be connecting even my smaller (sometimes very tiny) components too. Currently, I am using the store in many, but not all, components.
connecting smaller components is more performant because when Redux state changes, only the affected components will rerender. Passing down regular React props is less efficient because when state changes, that change gets passed through many nested components down to the small component, causing all those components to rerender.
While connecting small components is more performant, it comes with some drawbacks described in this answer. To summarize, connecting a component couples it to the Redux state. So, the component must be tested with a store, and the component can't be modular unless store implementation goes with it. The solution to this is to separate a component's stateful logic from its stateless logic.
const StatefulTodo = ({ id }) => {
const todo = useSelector(state => getTodo(state, {id});
return <StatelessTodo {...todo} />
}
const StatelessTodo = ({ ...todo }) => {
return (
<div>
<p>{todo.title}</p>
<p>{todo.description}</p>
...etc
</div>
)
}
With this approach, a Todo UI can communicate application state while being decoupled from the state, testable in isolation, and reusable.
In practice, you should decide which components to connect given the tradeoffs. A perceivably performant app doesn't have to prevent every possible unneeded rerender. And you can always use the React devtools profiler to see which components need to be optimized.
I know It may sound like a dumb question, But I am not able to get this solved in my head. Please bear with me.
In case when we use a state management system in React like Redux / Mob X, I guess the main purpose of these state management techniques is to provide a single source of Data and a more structured approach of updating it.
Say, I am Using a state management library(MobX) for React, And suppose I have a parent component which makes an http API call and updates the MobX store with the API response. Now I need that data in one of child/nested components.
My Question is, Should I pass that data as a prop to child component or should I enable child component to connect with Central Store and directly get that data ?
by connecting the child to store, I am turning the Child into a class component, which is making it more heavy and React optimisations may not apply. I am defeating the whole purpose of a functional component.
Awaiting replies.
Best Regards,
Lalit
This completely depends on the situation. I would suggest splitting your components up in 2 parts:
Components that could be re-used in other projects
(Higher level) Components that are so specific to this project that they probably never will be re-used.
For components of category 1, I would suggest not using mobx store directly, but instead make pure react components. (eg think of a dropdown, or an ajax dropdown component).
For second part components (think of, header, footer, section components specific for your website). just make them directly interact with the Mobx store, so that you can way quicker code what you need (instead of constantly having to prop everything).
addition
For components of category 1 you can always wrap them with the #inject() method. This way for example you could turn a dropdown component into a UserDropdown component that uses the mobx store for its state. (The inject method injects mobx state as props in the component).
const UserDropDownComponent = mobx.inject(stores => ({users: stores.userStore.users}))(DropDownComponent);
// usage:
<UserDropDownComponent />
Warning
For pure components wont always see changes of mobx state. To Fix this you need to wrap the component in an #observe annotation. Or you have to inject the props by wrapping it into: mobx.toJS(yourMobxStateProperty)
Coming with php background not sure how to implement a design with reusable code,
the problem:
we are creating a react/redux app with a lot of similar components that share a lot of functionality... I've read about HOC but was wondering if there are another solutions that would help in this situation!?
Component inheritance.
You can extend one component from some ComponentBase to share some functionality between them. But actually its different way from creating HOC. Hoc just allows you to pass some props to child, inheritance give you an ability to share methods and state.
There is something called function as child components (not sure if that's an official name but anyways)
The basic idea is that react accepts functions as child components, so for example instead of the react redux's connect HOC component you could do something like this
<Connect mapStateToProps={myMapper}>
{({ users, isLoading })} => {
//renders the user
}}
and the connect implementation is quite simple compared to a HOC.
render() {
return this.props.children(
this.props.mapStateToProps(this.state)
)
}
You can check a pretty interesting video about it here: https://www.youtube.com/watch?v=BcVAq3YFiuc
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.