George Hanna
George Hanna

Reputation: 392

React JS how to avoid deep callbacks

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

Answers (4)

Dawlatzai Ghousi
Dawlatzai Ghousi

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

ADJenks
ADJenks

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

C&#233;sar Landesa
C&#233;sar Landesa

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

Anatoly Strashkevich
Anatoly Strashkevich

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

Related Questions