I have this (simplified) structure in my React app:
App
|
---------
| |
Child1 Child2
I want to pass the data from Child1 to Child2 (for example clicking button in Child1 will change colour of one element in Child2)
Two important points here:
I want to avoid Redux if possible
I don't want to re-render the whole app. Only Child2 should re-render
Is there any option how to achieve that (without Redux)?
Try to make each child "dumb" and just have them just present the UI. Anything that changes causes side effects (state changes / handling clicks, etc) is moved up to the parent element, and into a hook.
Child1 gets an onClick function (from the parent via the hook) and child2 gets whatever state variables affect its appearance.
If the children are within other children and you want to avoid "prop drilling", then using React.Context is lightweight.
Related
I am running some tests with react to find out a way for a parent to send an "event" to a child component. In the test application, the parent (CounterController) has a button. When the button is clicked, the parent shall send a reset "event" to the child (Counter) to reset the current count. Like below.
I am new to react, and can not find a way to dispatch/receive events or messages between components. I had a thought to mark the "reset" request as a "state" in the parent, then pass it to the child through props. Then the child clear the "reset" state, through a callback function, to avoid repeating "reset" requests. However, I got the code run into an infinite loop. Here are my codes.
The parent component - CounterController
The child component - Counter
the error logs
I do not feel that I am using react the right way, by asking the child to do something from the parent. Anyway, any idea about how to implement this app in react? How should the data flow be built correctly? Thanks!
You can move up the counter state on the parent ‘CounterController’
and then you pass the counter and setCounter as props to ‘Counter’
You don’t need ‘reset’ state
I am posting my thoughts about using react (as a beginner), and my solution to the app.
treat the react components as the "view" of the application, and maintain the data and controls in the hooks.
create a custom hook to wrap up all data and controls.
create an instance of the custom hook in the top component - display the data in HTML elements and bound controls to buttons to build the app UI
Here are my codes for this simple UI
UI
useCounter hook
counterController component
counter component
Move "count" state from child component (Counter) to parent
component (CounterController).
Pass count state as prop to child component.
Pass increment and decrement methods from parent to child component through props.
Note: Remove reset state, it's not required anymore.
Sandbox link : https://codesandbox.io/s/infallible-platform-vo4qtv?file=/src/CountController.js
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?
Is there any easier way to get ref from a child component and use it in the parent component without going down to deep in the nested components?
Imagine you have a parent component and the targeted child is like 5 level down. Then I have to access it like this from the parent component.
this.myRef.current.firstChild.firstChild.firstChild.firstChild.firstChild.firstChild
I have two components in my react application, one is parent and the second is the child.
I pass fakedata state as a props to child and then in child component save this as state of child component. But when I change something in child it's affect on parent state and I don't want this I want it's only effect on child state.
This is how I call child component from parent :
<FakeDataAddEditComponent {...this.props} fakedata={{...this.state.fakedata}} />
and in child component this is how I set fakedata props to state :
componentDidMount() {
this.setState({fakedata:{...this.props.fakedata}},()=>{
})
}
but when I change fakedata state in child it's also changend in fakedata of parent and I don't want this.
To answer your question directly, the reason you are seeing this behaviour is because Javascript passes a reference to the original object down the props and to the state of child component. When you update the child component it is the same instance as the parent is holding.
To fix the problem you should use Object.assign to make a copy of the object, however keep in ming that you will run into problems with nested objects.
Also, if parent object mutates the state in any way after the child state changed it will pass old object as props to the child.
In general you're trying to do something you shouldn't do because you will run into trouble.
On a high level you should go for a proper state management solution like Redux of Flux.
I know the main concern about when to put something on the state or receive as props, but what about when I have something that is suitable for both? I have items that should be 'active' when the parent is active(props) and also have the option to toggle this attribute from a click event(state).
It sounds like what you want is something like a parentActive variable held in the state of the parent and passed as a prop to the child(ren). Then a childActive variable held in the state of the child that is toggled by your click event. Then you'd simply have some logic / a conditional within the child that determines whether it's active. For example:
// Within child component
if (this.props.parentActive || this.state.childActive) { }
In general, when you have a new value that needs logic, you store it in state, create the click handlers that you need within that component, then pass it off (and possibly its click handler) as a prop for children. In your case, you need logic for a value/active flag within the parent, and logic for a value/active flag in the child.. so it sounds like you would want to set state in both of them, and pass the parent value as a prop for the child.