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
显然变得简单多了。