You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
为了展示更加简短,下面这段代码是删减后的 TodoList 组件,App 组件负责定义 todo 参数与组件渲染。TodoList 组件负责渲染 TodoList 整体结构。TodoItem 组件负责渲染单条 Todo 数据。需要做的是用 HOC 和 Compose 去改进它。
functionTodoList({todos, isloading}){if(isloading)return<div><p>Loading ...</p></div>;if(!todos)returnnull;if(!todos.length)return<div><p>You have no Todos.</p></div>;return(<div>{todos.map(todoItem=><TodoItemkey={todoItem.id}todo={todoItem}/>)}</div>)}functionTodoItem({todo}){return<div>todo: {todo.content}</div>}classAppextendsReact.Component{state={todo: [{id: 1,content: 'eat some cake'},{id: 2,content: 'drink some juice'}{id: 3,content: 'take a break'}],isloading: false}render(){let{isloading, todo}=this.statereturn(<div><TodoListisloading={isloading}todos={todo}></TodoList></div>);}}
以上是基础代码,先看 TodoList 这个函数,定义一个函数 withTodosNull,目的是分离 if (!todos) 这个判断是否存在 todos 的逻辑做成 HOC。HOC 名称一般以 with 开头。
// ConditionconstisTodosNull=(props)=>!props.todos;constisTodosEmpty=(props)=>!props.todos.length;constisTodosLoading=(props)=>props.isloading;// EitherCompconstemptyTodos=()=>{return<div><p>You have no Todos.</p></div>}constloadingTodos=()=>{return<div><p>Loading todos ...</p></div>}// HOCconstwithMaybeNull=isNull=>Comp=>(props)=>{//...}constwithEither=(conditionalRenderFn,EitherCom)=>(Comp)=>(props)=>{// ...}consttodosWithHOC=compose(withEither(isTodosEmpty,emptyTodos),withMaybeNull(isTodosNull),withEither(isTodosLoading,loadingTodos))// ComponentsconstTodoList=(props)=>{// ...}constTodoItem=(props)=>{// ...}// render AppconstTodosRender=todosWithHOC(TodoList)classAppextendsComponent{//...}
关于写 HOC 需要注意的可以阅读官网文档,包括为了在 Chrome 调试方便添加 displayName,禁止在 render 中使用高阶函数等等。其实在这篇文章中写的 HOC 为了简约代码是返回了一个匿名函数,实际开发中更多写的是一个 Class 类除非是木偶组件,或者将 HOC 单独放在一个 React 文件中导出引用。
高阶组件(HOC)本质是高阶函数,传入参数变成 React 组件返回一个新组件所以称为高阶组件,核心在于抽离公共逻辑。本文用友好的方式去演示如何将普通的组件代码修改成 HOC。在个人的应用场景中,只要有多个组件或者功能逻辑可复用的话那么做成 HOC 是比较好的选择。或者常用的 React-Redux 中 connent 连接函数就是一个 HOC。
为了展示更加简短,下面这段代码是删减后的 TodoList 组件,App 组件负责定义 todo 参数与组件渲染。TodoList 组件负责渲染 TodoList 整体结构。TodoItem 组件负责渲染单条 Todo 数据。需要做的是用 HOC 和 Compose 去改进它。
以上是基础代码,先看 TodoList 这个函数,定义一个函数
withTodosNull
,目的是分离if (!todos)
这个判断是否存在 todos 的逻辑做成 HOC。HOC 名称一般以 with 开头。以上就是一个简单 HOC,可以利用箭头函数对其进行更简略的写法,然后将判断 todos 数组是否存在、是否为空、是否在加载状态这三个逻辑判断都改为 HOC:
由于这里每个 HOC 返回的是 React 组件,匿名返回函数中接收的参数是 props ,所以在
withLoadingIndicator
函数中返回的匿名函数,可以用结构赋值和扩展运算符的方式接收变量。初步改造完成后代码就变成了这样:在将基础组件传值给 HOC 处理的时候,由于有多个 HOC 所以需要将组件逐个传递,这样的做法非常繁琐,另一种可行的方法就是将其改进为:
但如果需要用到更多的 HOC,那这种嵌套阅读性几乎为0。更好的解决方法是使用
compose
组合函数。这里简单写写关于 compose 的内容,也当做是一个简短的笔记。使用 compose 的目的在于将多个函数组合成一个函数,直接调用一次即可完成多次调用,这样就显得代码不会变的繁琐。一个简单的 compose 函数如下:然后写两个简短的函数利用 compose 组合并尝试调用
这个函数缺点在于只能接收2个函数作为参数,如果组合需要多个函数就得把 compose 改进一下通过 while 遍历执行传入的多个函数就可以了:
这里将 compose 用变量函数的形式引入到刚刚的组件代码中,就可将繁琐的步骤移除。
改到这里基本就用 HOC 去分离出了所有逻辑代码。接下来重点就是组件的可复用性。先看
withTodosNull
高阶组件里的代码判断渲染逻辑:这个判断是否为 null 逻辑不仅仅是 TodoList 这个组件用到,其他组件参数如果为 null 也不执行的话那么这段逻辑是可以复用的,所以可以将
withTodosNull
这个 HOC 拆分为isTodosNull
和withMaybeNull
:这里我将原来的 !props.todos 单独分离了出来作为一个函数
isTodosNull
,这样withMaybeNull
里就不是判断 props.todos 这个固定值,其他组件也可传入不同的函数参数进行判断,withMaybeNull
就变成任何组件都可以用的 HOC。修改好
withMaybeNull
后,可以看到另外的两个withTodosEmpty
和withLoadingIndicator
HOC组件有一个共同点,它们都是根据判断数据不同的情况从而特定展示信息组件或者传入的组件。所以可以根据这个逻辑来定义这样一个公共 HOC:withEither
HOC第一个参数是条件判断方法,第二个参数是条件判断方法成立后返回的组件(EitherCom),否则返回原组件(Comp)。HOC 写好后就可以修改代码了:可以看到,
withTodosEmpty
和withLoadingIndicator
HOC都已经移除,在 compose 组合函数中也替换为了新写的withEither
HOC函数,传入判断函数和判断方法成立后返回的组件既可,到这里代码的整体改进全部完成。完整代码可以在Github TodoList Demo查看。在实际项目开发中,我个人是比较困难去做到对每一个组件都进行非常细微的拆分和重构处理,但如果写组件的时候多注意多使用这样的方式去写抽离共用逻辑的话,那么维护起来至少比普通组件要方便。更希望是能通过这种简单的思路能引起平时写组件代码时对复用结构的思考。还有一种常用到的 HOC 形式:
在这个例子中,
makeProvider
这个 HOC 接收一个任意类型的数据和组件作为参数并返回一个匿名类,目的在于使得CompTest
下的所有子组件都可以通过this.context.data
获取到传给makeProvider
的参数。这里只是为了演示用,官网并不推荐使用 Context。关于写 HOC 需要注意的可以阅读官网文档,包括为了在 Chrome 调试方便添加 displayName,禁止在 render 中使用高阶函数等等。其实在这篇文章中写的 HOC 为了简约代码是返回了一个匿名函数,实际开发中更多写的是一个 Class 类除非是木偶组件,或者将 HOC 单独放在一个 React 文件中导出引用。
额外内容
一个小 Tips 是关于 React-Redux 的,下面是一个非常简单的例子,获取day和设置day的函数并用 connect 将这两个函数和
CompTest
关联上:利用 ES7 的新语法 Decorator 后,可以将这段代码改成下面这样显得更加简易:
The text was updated successfully, but these errors were encountered: