如何避免向下传递回调?

359 阅读2分钟

React

我们已经发现大部分人并不喜欢在组件树的每一层手动传递回调。尽管这种写法更明确,但这给人感觉像错综复杂的管道工程一样麻烦。

在大型的组件树中,我们推荐的替代方案是通过 context 用 useReducer 往下传一个 dispatch 函数:

const TodosDispatch = React.createContext(null);

function TodosApp() {
  // 提示:`dispatch` 不会在重新渲染之间变化
  const [todos, dispatch] = useReducer(todosReducer);

  return (
    <TodosDispatch.Provider value={dispatch}>
      <DeepTree todos={todos} />
    </TodosDispatch.Provider>
  );
}

TodosApp 内部组件树里的任何子节点都可以使用 dispatch 函数来向上传递 actions 到 TodosApp

function DeepChild(props) {
  // 如果我们想要执行一个 action,我们可以从 context 中获取 dispatch。
  const dispatch = useContext(TodosDispatch);

  function handleClick() {
    dispatch({ type: 'add', text: 'hello' });
  }

  return (
    <button onClick={handleClick}>Add todo</button>
  );
}

总而言之,从维护的角度来这样看更加方便(不用不断转发回调),同时也避免了回调的问题。像这样向下传递 dispatch 是处理深度更新的推荐模式。

注意,你依然可以选择是要把应用的 state 作为 props 向下传递(更显明确)还是作为作为 context(对很深的更新而言更加方便)。如果你也使用 context 来向下传递 state,请使用两种不同的 context 类型 —— dispatch context 永远不会变,因此组件通过读取它就不需要重新渲染了,除非它们还需要应用的 state。

Vue

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。

如果父组件中有很多事件。

<div id="parent">
  <child @change="handleChange">child</child>
  <p>父组件</p>
</div>

那么内部组件可以通过 v-on="$listeners" 往下传递。

<div id="child">
  <son v-on="$listeners">child</son>
  <p>内部组件</p>
</div>

在下层组件 son 可以通过 $listeners 调用上层传递的事件。

methods: {
  change (index) {
    this.$listeners.change(index)
  }
}

通常是通过$emit进行派发,但是如果有多层级嵌套的组件,每层都要派发事件,这样显得额外麻烦,而通过$listener显然变得简单多了。