I'm using a React component library and populating a modal (Parent) with a complex stateful component (Child). This generic modal also has a footer with a save button. The user interacts with the contents of the modal (Child) and when they are ready, they click the modal. This saves to the server, the current state of the internal component.
I'm trying to understand the best way to accomplish this in React. Right now, the child component is using useImperativeHandle to expose a getData() function that is used by the click listener callback in the parent modal who then calls the server. This feels wrong. What I'd like is the parent component to tell the child to save its contents when the user clicks on the modal save button. I don't think the modal should contain the save logic that contacts the server.
Please let me know what are the best practises for this case.
Thanks!
Agree, it does feel weird for the parent to request data from a child at the time of submit, so data ownership should either be in the parent or at a shared data source (initialized in the parent).
On the parent side, not sure of how many changes may get fired by the child (perhaps when the process is completed), but thinking you could have something like an onStuffChanged callback on the child that sets values in the parent to use on the submit.
On the shared data source (keeping it to React), you have a reducer or context. Context might be overkill here since it's just a parent / child relationship.
Will use reducer as an example here, but there are many other libraries that could solve this with state management. Basically you can pass the dispatch to the child and have it update the data store as needed (i.e. when it has all the data needed to submit). The parent submit button can then use the data stored there.
Basically suggesting either a child callback or shared data source / state management pattern that the parent and child have access to.
Related
I have an application which on page load, fetches data(an array of objects) from database and displayed it as rows in UI. Each row is a child component which can be created, edited, deleted or reordered. The parent component has a Save button which gets the data from all child components and updates it in database.
There seems to be 2 ways to store this data:
Store all data in the parent component:
The parent component fetches the data, stores it in a state variable and passes it on to the child components. When data in child component changes, it is directly updated in the parent component. When we need to save, the whole state variable in parent can be written to database as it is.
Pros:
All data is maintained in parent as it should be in the database. So fetch and save are straightforward.
Child components are dumb and only display data in UI. Good separation of smart data-components and dumb presentational-components.
Cons:
Whenever a child updates data, parent's state var updates and so all child components re-render since they share the same state data var(am I wrong here? Does react handle arrays state var's element passed as props smartly?). This could be a problem if the list grows long and there are a lot of child components.
The parent component ends up doing almost everything, making it quite complex.
Store each row data in child component:
The parent component fetches the data, and passes it on to the child component. The child component maintains its own data and when data in child component changes, only updates it in its local state var. When we need to save, the parent component will have to ask each child component to provide its data, collate it into an array and update it in the database.
Pros:
On changing a row data, only that child component is re-rendered.
Child components handle their own data, making parent component simpler.
Cons:
Distributed data across child components means collating them for database writes are a pain.
On clicking save button, calling a function in child from parent using refs, which returns child data is not a recommended react pattern.
In React is recommended that the parent component handles the data and distributes that data to children. To avoid re-rendering every child component I would take a look at memo(): https://reactjs.org/docs/react-api.html#reactmemo. This will cache your child component, and decides if it needs to be re-render or not.
To implement this with an Array I would check out this: How do I apply React.memo to all components in an array?
I have a form (Parent) which contains an input field (Child), which gets its value from a reference table (Grand-grand-child) that is displayed as a modal (Grand-child) which opens up by clicking a button attached to the input field. This is a nested structure that roughly looks like this:
I need to set the value of the input field by selecting a row in the reference table and confirming my choice with a button "SET VALUE", which means I need to pass data three levels up from Grand-grand-child to Parent through Grand-child and Child.
My state is kept in the Parent component. Is there a simple way of achieving that without using external libraries? Please offer a solution using Hooks as all of my components are functional components.
Here is the code: https://codesandbox.io/s/festive-fast-jckfl
See CreateRate component where:
CreateRate.jsx is the Parent
InputField.jsx is the Child
DataFetchModal.jsx is the Grand-child
Airports.jsx is the Grand-grand-child
Pass a change handler function from parent (where state lives) down to the grand child. The grand child should call this change handler when clicking the Set Value button.
If this is too much prop drilling
look into component composition first
if that doesn’t work out, look into context api
Update:
You mentioned your problem was trying to access the state inside Grand-grand-child from your Grand-child. In this case you can lift the state up (to Grand-child). This means lifting 'airports' up to DataFetchModal. Here is more info on lifting state.
https://reactjs.org/docs/lifting-state-up.html#lifting-state-up
Also, it appears you are running into these problems because your code is very nested and not very composable. I would suggest looking into how you could better break up your components. One way to accomplish this is using compound components.
https://kentcdodds.com/blog/compound-components-with-react-hooks
Determining how to break this up better would take some time. However, just looking at this briefly. You may be able to move your DataFetchModal to your parent. Pass a callback to the InputField to fire it off and send the identifying parameters (what input called it). Then, from the parent, compose in whatever modal body using composition. It appears you have a lookup object (objType) inside DataFetchModal. Maybe this could go away by using composition (not sure, and probably separate topic).
Update 2:
I pulled down your code sandbox and made a few changes that may help access parent level state. I moved the modal into the parent. Check it out.
https://github.com/nathanielhall/Test
Parent component provides data to child component (that displays it) (have multiple children). The child component has 2 buttons connect and disconnect. When connected to should receive and show the data, when disconnected clicked, it should disconnect from data.
How I would do it: child calls method in parent for connect/disconnect,then i would have a flag in parent state and based on that either render child with or without data, then inside child i render props or null if no props provided.
That would mean if I have 10 child components, I would need 10 flags which doesnt seem good, so I 2nd idea:
having the flag insight the child and method for triggering connect/disconnect, render based on if props were provided
My question is, is there a good practice react way of connecting children to parents data flow/stopping data flow without always passing data to children and just rendering/not rendering it based on if child 'wans' to be conencted or not?
Thanks!
Did you try using Redux or Context API?
The two are very similar and it should get what you want.
They basically use 1 store(think of them multiple states are connected to 1 store) and you get data from child components.
After getting your data you can check if user is logged in or not and put your buttons according to this data.
If I was not clear enough feel free to ask any question.
Also official docs for Redux and Context API are:
https://redux.js.org/introduction/getting-started
https://tr.reactjs.org/docs/context.html
The web is full of people telling me I'm doing react wrong. I'm sure they're right, since I've just started. A commonly referenced truism is that react is just the view layer. That's wonderful, but it leaves me wondering simply where do I put my other stuff?
I have a pretty straight-forward set of components which together show a list of nested data types and allow some editing, creation, and removal of the things. The components which consume the data have it passed in through props down the hierarchy from a parent that fetches the data. Should those read operations go somewhere else if it's "just the view layer"
What's the best practice for this, the idiomatic way, the react way? Should my delete operation be included with my delete button component? next to the read operation? somewhere else?
I suggest you to try some flux architecture. In this case it will looks similar to described below:
You call action on delete button click, for example actions.deleteUser(1)
Action calls appropriate API
Store connected to your component changes and component state changes
component re-rendered automatically
It depends on Flux that you prefer. The flow may be slightly different. I prefer reflux one
In react, data flows only from parent component to child component. I assume that your state is stored in the parent component, since you have create, update and delete in separate components. The state can only be changed by calling the setState() function on it. Therefore child components cannot modify the state of the application. This can be done only by having a callback function.
The delete function will be written in the parent component and will be passed to delete component as a prop. The delete component will then call the delete function (callback) which will call the setState() upon the state of the parent component and thereby altering the state. Never alter the state of the application without setState().
Hope this helps
Should child components never have a state in React? I understand that state should be maintained by the "wrapper container" or parent container and it should have unidirectional flow. I have started with React and have a header container with upto 10 child components.
Let's say one of the child component is a Form with a submit button that can be enabled or disabled.
Should this child component not be able to have a constructor with state initialized for the button and be able to directly manipulate it or is it important that I maintain states as minute as this in the wrapper container "only"?
You should let the parent container manage the state of forms. I usually attach an onChange listener to each input and then when the submit button is clicked I call a function in the parent component to submit the value contained in the state for my form inputs. The form should only render inputs and do nothing else, basically a dumb component.
The purpose of React is providing a component system to front-end. It does not specify/enforce how state flow. People generally prefer state-less components because it is easier to share and distribute. However, front-end component can never be fully state-less + declarative.
In my opinion, you should feel free to use this.state to manage local state when you feel appropriate.