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
Related
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.
Whenever I use Frameworks such as React , Vue or anything else. I see that every value (input value, select value ...) is always being emitted through events to parent. I don't really get why this is good. It takes more ressources / time to pass it to the parent component. Why doesn't the component handle its data by itself?? Why should every single value inside a child component get passed to the state of the parent to make the handling of the data ?
For example, I generally see the loading of the APIs in the parent and then the values of the API get passed down through props or Redux/Vuex. Why can't we get the API values inside the child directly when it's being mounted instead of having to pass it down on many levels or through the local store
It's perfectly valid to have a state inside reusable component, especially if it holds users input. You can still provide it with callbacks in props, which respond to onChange, onBlur or other events based on your needs, or you can communicate with your reusable component via refs, which is fairly flexible way to get user input.
The important thing is, that the reusable component should be as much independent on it's environment as possible. If you don't provide it with a state, the component (in case of your Pickers) will be dependent on props passed down from its parent expressing it's current user input on every rerender and on callbacks updating its current input somewhere higher in component hierarchy on every change, which is not what you want in every case, and it already forces you to make relatively large adjustments in you parent component, which misses the point of reusability.
There is some related threads, and a lot of opinions on this subject so I will try to summarize my findings. Notice I am a beginner so this is my best bet, and the data I gathered from related posts and this.
In general most people agree that if the parent call's a child it is bad practice. In the related post for this article there is an accepted answer, where the person answers that it is a bad practice but possible. #mat sz answer directed me on the right path. I have updated my sample for others to see. I pass a parameter to my child component. In the child component I add a listener for changes on that parameter:
useEffect(() => {
if (props.toggleClose != null) {
setToggleJobTable(false);
}
}, [props.toggleClose]);
I just toggle that parameter between true/false in my parent. Not beautiful but my best bet on how to do this in react.
Original question
I found a lot of samples in here on how to pass variables to child components, how to raise events from child components to parent components, and even how to pass a function to a child component from parent component. But what about last scenario. How to call a function in a child component from the parent. In this sample I would like to trigger the closeAll function in the child component from the parent:
https://codesandbox.io/s/toggle-all-qxkgy?fontsize=14&hidenavigation=1&theme=dark
I still dont have an answer on this, but a lot of good input in the posts. For me it sounds like the right approach here would be parsing a parameter to the child as parameter. My question is then, if I pass a parameter to the child component. How do I toogle when the parent changes that value, then my closeAll function in the child is called?
It is also stated that it could be wrong design. If you look on my very simple sample how should it else be designed? All input are welcome. I have very little experience with REACT so I am trying to understand how an experienced REACT developer would solve this simple task/sample.
It's not possible, parent components are not able to access the functions inside of child components.
You could use something like Redux or MobX to create a global state and then rely on global state for that, or you, ideally, should restructure your code in a way that does not require this, for example if your function (the one that has to be called inside of parent) changes state of a child component - move that state to the parent component, and then provide that state via props to the child component (along with a setter function, if required).
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
As far as I can tell, if I pass a parent component state down to a child, then that child gets the live state of the parent.
So a change made in the state of the parent is immediately also available in the child via the prop that it came on.
Is this correct?
It's basically the same mechanism as anywhere else in the language, as you'd expect. Primitives are passed by value and variables that aren't primitives will be passed by reference.
React takes care internally of the updating of props, so that children always have the most up-to-date value of the prop.
This is the lifecycle method that is called when receiving new values for props.
However, make sure you respect the infrastructure put in place and the exposed API that React gives you.
Short answer: props are passed by reference.
The reason it can be confusing: if you change the parent's state by hand (bad practice!), the object will change in the child component, too. But won't trigger re-render! (The child component won't "know" that its props has changed.) So you won't see the UI change.
However if you change the parent's state with setState (the preferred practice), the child will be notified, that it must re-render itself.
If you pass down the state of the component as props to its child, then if the state of the parent component changes it re-renders, which will also re-render its children with the refreshed properties. The children don't directly listen for the state change like the parent does, they are simply re-rendered as as result of its parents state change and updated.
Take a look at this - https://facebook.github.io/react/docs/multiple-components.html. It will help you get your head round how this concept works. Hope this helps!
When the state of a component is changed, then the component is re-rendered by React. While doing that , its child components are also re-rendered, which causes changes in them also.
No, they will not be duplicated, you will access to those props by reference, because they come from a single object wich defines them, and then pass them as a reference to the child objects.
You can take a look to the official documentation here: https://reactjs.org/docs/react-component.html.
I suggest to use a stateless mechanism to handle large data, expecially if shared.
Personally I use mobx (https://github.com/mobxjs/mobx) wich is a great framework to create stateless apps.
With this method you can handle data and state updates in a single component, called Store, and use components to render html only, and not to handle data, wich is a great boost on application performances.