Reputation: 392
My question is "Is there a way to avoid sending callbacks deep in the tree when I want to access/react upon child components data".
I have a component Application
that contains a child component Person
which in turn contains a child component TraitCollection
which in turn contains a child component Trait
.
When Trait
is changed I want to send data back to Application
to be forwarded to another child in Application
named PersonList
.
My current solution is to send callbacks as props to each child. Each callback function constructs and appends the data which finally reaches Application
where I pass it down as a prop to PersonList
.
I can imagine that "adding more levels", or splitting components up in more smaller parts will further complicate this callback chain.
Is there any other way to do this or is this the best way to handle passing data from children to parents in React?
Upvotes: 6
Views: 2212
Reputation: 3978
To avoid Deep Callbacks
, according to React documentation, useReducer via context
as below:
How to avoid passing callbacks down?
We’ve found that most people don’t enjoy manually passing callbacks
through every level of a component tree. Even though it is more explicit, it can feel like a lot of “plumbing
”.
In large component trees, an alternative we recommend is to pass down a dispatch
function
from useReducer via context
:
const TodosDispatch = React.createContext(null);
function TodosApp() {
// Note: `dispatch` won't change between re-renders
const [todos, dispatch] = useReducer(todosReducer);
return (
<TodosDispatch.Provider value={dispatch}>
<DeepTree todos={todos} />
</TodosDispatch.Provider>
);
}
Any child
in the tree inside TodosApp
can use the dispatch
function
to pass actions up to TodosApp
:
function DeepChild(props) {
// If we want to perform an action, we can get dispatch from context.
const dispatch = useContext(TodosDispatch);
function handleClick() {
dispatch({ type: 'add', text: 'hello' });
}
return (
<button onClick={handleClick}>Add todo</button>
);
}
This is both more convenient from the maintenance perspective (no need to keep forwarding callbacks
), and avoids the callback
problem altogether. Passing dispatch
down
like this is the recommended pattern for deep
updates
.
Upvotes: 0
Reputation: 3424
The docs answer this directly:
In large component trees, an alternative we recommend is to pass down a dispatch function from useReducer via context
From: https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down (Archive)
Upvotes: 0
Reputation: 2656
The way you are handling it is the recommended way and the most React-like. There is nothing wrong with that approach by itself other than the problem you have found. It looks like Redux could help you solve that problem. Redux lets you unify the state of your React application with the usage of a common store and a good design pattern based on action creators, actions and reducers.
Upvotes: 3
Reputation: 1914
You can use React Context directly https://facebook.github.io/react/docs/context.html, but better option will to use React-Redux, it will allow you to avoid passing callbacks and you will be able to change state of any component from any component (connected to the store) with dispatch function.
Upvotes: 0