5

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?

George Hanna
  • 382
  • 2
  • 15

4 Answers4

3

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.

César Landesa
  • 2,626
  • 1
  • 13
  • 18
  • Thanks for the help, will look into Redux! – George Hanna Aug 30 '17 at 08:40
  • 2
    Why do you think "there is nothing wrong with that approach"? if one ever changes the name of the callback, one must change the who-knows how many files which are all a tangled web of useless props where only the beginning and the end matter and everything between can only create potential complications and even break the whole delicate chain by a single mistake in one of its links. – vsync Aug 22 '18 at 17:14
0

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.

Anatoly Strashkevich
  • 1,834
  • 4
  • 16
  • 32
0

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)

ADJenks
  • 2,973
  • 27
  • 38
0

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.

Dawlatzai Ghousi
  • 3,780
  • 2
  • 20
  • 26