We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
对某些逻辑进行抽象。抽象之后方便复用、方便维护。
高阶函数 - 维基百科:接受一个或多个函数作为输入或者输出一个函数。
比如 array.sort() ,是可以传一个函数来自定义如何排序的。
再比如 redux 的 compose 函数,接收和返回都是函数, compose(f, g, h) 的效果等同于 (...args) => f(g(h(...args))) 。
compose(f, g, h)
(...args) => f(g(h(...args)))
来一个项目中实际应用的例子。比如有很多表单字段都是数字,然后需要校验整数位和小数位长度。 antd 表单校验是支持传一个 validator 函数来做自定义校验的,那我们可以封装一个函数,传入参数是整数位和小数位长度,返回值就是一个 validator 。
numValidator 的具体实现如下:
// 整数最多 integer 位,小数最多 decimal 位的非负数 function numValidator(integer, decimal) { // 这里返回了一个符合 antd 表单格式的 validator 函数 return function validator(rule, value) { if ([undefined, null].includes(value)) { return Promise.resolve() } const reg = new RegExp(`^[0-9]{0,${integer}}(\\.[0-9]{0,${decimal}})?$`) if (reg.test(String(value))) { return Promise.resolve() } return Promise.reject( `请输入整数最多${integer}位,小数最多${decimal}位的非负数` ) } }
在表单中使用:
<Form.Item name='num' label='数字,测试校验' // 传入整数和小数位数 rules={[{ validator: numValidator(2, 1) }]} > <InputNumber style={{ width: '100%' }} /> </Form.Item>
Higher-Order Components – React:a higher-order component is a function that takes a component and returns a new component.
React 文档中定义高阶组件为一个接收一个组件然后返回一个新组件的函数。
比如 react-router 中的 withRouter , withRouter(Hello) 会给 Hello 组件增加 match, location, history 属性。
withRouter(Hello)
来一个实际应用的例子。我有一个详情页面,里面有很多表单卡片,每个表单卡片有编辑状态和查看状态。当我离开页面时,如果有编辑状态的卡片,我希望能进行一个是否确认离开的提示。
你可以把这个功能和你页面的逻辑写在一起,但是其实这是一个相对独立的功能点,所以你可以选择把这个功能点封装一下,下面是用高阶组件方式的实现。
import { useRef } from 'react' import { Prompt, useHistory } from 'react-router-dom' import { Modal } from 'antd' // 会在离开页面时,判断一下是否有未保存的卡片,有的话就进行提醒 function withLeavePrompt(WrappedComponent, offsetTop = 0) { return function (props) { const history = useHistory() const editingRef = useRef([]) const addEditing = (title) => { editingRef.current.push(title) } const removeEditing = (title) => { editingRef.current = editingRef.current.filter((t) => t !== title) } const onPrompt = (nextLocation, action) => { if (!editingRef.current.length) { // return true 表示允许直接离开页面 return true } Modal.confirm({ title: '还有未保存的数据,确定离开当前页面吗?', // 点击取消的话,就自动滚动到第一个没保存的卡片 onCancel: () => { const element = document.getElementById(editingRef.current[0]) // copy from antd components/anchor const scrollY = window.pageYOffset + element.getBoundingClientRect().top - offsetTop window.scrollTo(0, scrollY) }, // 点击确定的话,注意要先 editingRef.current = [] ,因为会再次进入 onPrompt onOk: () => { editingRef.current = [] if (action === 'POP') { history.goBack() } else { history.push(nextLocation.pathname) } }, }) // return false 表示依然停留当前页面 return false } return ( <> <Prompt message={onPrompt} /> {/* 给组件增加了 addEditing 和 removeEditing 属性 */} <WrappedComponent addEditing={addEditing} removeEditing={removeEditing} {...props} /> </> ) } } export default withLeavePrompt
使用的时候就 withLeavePrompt(Detail) ,然后 Detail 就会有 addEditing 和 removeEditing 属性了,当表单卡片点击编辑的时候就 addEditing ,点击取消或者保存的时候就 removeEditing 。这样离开页面时,withLeavePrompt 里面的逻辑就会去检查当前有没有编辑状态的卡片。
withLeavePrompt(Detail)
这样子抽象了之后,其它页面如果也要做这个功能,就直接用就可以了,如果这个功能要做调整,也只用去修改 withLeavePrompt 中的代码,优点也就是开头说的,方便复用,方便维护。
Render Props 其实就是组件的属性为一个函数,函数接收值可能是组件内的状态啥的,然后返回一个 jsx 。一般需要做自定义渲染的地方会用到,比如 antd Select 的 dropdownRender 属性。
高阶组件也可以用自定义 hook 去实现,比如我们上面写的 withLeavePrompt ,也可以改为 const [prompt, addEditing, removeEditing] = useLeavePrompt(offsetTop) ,但是就这种场景来说,感觉用高阶组件会省事点?会吗?
const [prompt, addEditing, removeEditing] = useLeavePrompt(offsetTop)
之前我们还说到高阶组件 withRouter ,那其实 react-router-dom 有提供 useHistory 和 useLocation 等来获取 history 和 location ,这种时候我感觉自定义 hook 会更舒服。
怎么说呢,我是感觉自定义 hook 会更适用于单纯逻辑上的抽象,如果说需要改组件 return 的 jsx ,用高阶组件会省事点。
用自定义 hook 的一个很大的优点是代码很明确,就比如 const history = useHistory() 这样的代码你就知道 useHistory 返回的就是 history ,而用高阶组件时你可能要看看这个组件的文档或者代码实现你才知道它对原本的组件做了啥处理。从这个角度来讲,const [prompt, addEditing, removeEditing] = useLeavePrompt(offsetTop) 就会比 withLeavePrompt 好一些,如果你不看 withLeavePrompt 代码你不知道它会注入 addEditing 和 removeEditing 属性。
const history = useHistory()
这三者的一个对比可以见 Comparison: HOCs vs Render Props vs Hooks | Medium 。虽然这篇文章觉得万物皆可自定义 hook ,但是我觉得实际上还是要看场景来的,平时开发可以多试试这几种方式,从实战中感受下区别。
怎么去做抽象呢,可以把功能都先开发完,然后再想想哪些功能点是相对比较独立的,把比较独立的/需要复用的功能点给拆出去。当你比较熟悉这个过程后也可以选择在开发前期就先把抽象做好,就不用后面再拆了。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
目的/功能
对某些逻辑进行抽象。抽象之后方便复用、方便维护。
高阶函数
高阶函数 - 维基百科:接受一个或多个函数作为输入或者输出一个函数。
比如 array.sort() ,是可以传一个函数来自定义如何排序的。
再比如 redux 的 compose 函数,接收和返回都是函数,
compose(f, g, h)
的效果等同于(...args) => f(g(h(...args)))
。来一个项目中实际应用的例子。比如有很多表单字段都是数字,然后需要校验整数位和小数位长度。 antd 表单校验是支持传一个 validator 函数来做自定义校验的,那我们可以封装一个函数,传入参数是整数位和小数位长度,返回值就是一个 validator 。
numValidator 的具体实现如下:
在表单中使用:
高阶组件
Higher-Order Components – React:a higher-order component is a function that takes a component and returns a new component.
React 文档中定义高阶组件为一个接收一个组件然后返回一个新组件的函数。
比如 react-router 中的 withRouter ,
withRouter(Hello)
会给 Hello 组件增加 match, location, history 属性。来一个实际应用的例子。我有一个详情页面,里面有很多表单卡片,每个表单卡片有编辑状态和查看状态。当我离开页面时,如果有编辑状态的卡片,我希望能进行一个是否确认离开的提示。
你可以把这个功能和你页面的逻辑写在一起,但是其实这是一个相对独立的功能点,所以你可以选择把这个功能点封装一下,下面是用高阶组件方式的实现。
使用的时候就
withLeavePrompt(Detail)
,然后 Detail 就会有 addEditing 和 removeEditing 属性了,当表单卡片点击编辑的时候就 addEditing ,点击取消或者保存的时候就 removeEditing 。这样离开页面时,withLeavePrompt 里面的逻辑就会去检查当前有没有编辑状态的卡片。这样子抽象了之后,其它页面如果也要做这个功能,就直接用就可以了,如果这个功能要做调整,也只用去修改 withLeavePrompt 中的代码,优点也就是开头说的,方便复用,方便维护。
React 中常用的抽象方式
Render Props 其实就是组件的属性为一个函数,函数接收值可能是组件内的状态啥的,然后返回一个 jsx 。一般需要做自定义渲染的地方会用到,比如 antd Select 的 dropdownRender 属性。
高阶组件也可以用自定义 hook 去实现,比如我们上面写的 withLeavePrompt ,也可以改为
const [prompt, addEditing, removeEditing] = useLeavePrompt(offsetTop)
,但是就这种场景来说,感觉用高阶组件会省事点?会吗?之前我们还说到高阶组件 withRouter ,那其实 react-router-dom 有提供 useHistory 和 useLocation 等来获取 history 和 location ,这种时候我感觉自定义 hook 会更舒服。
怎么说呢,我是感觉自定义 hook 会更适用于单纯逻辑上的抽象,如果说需要改组件 return 的 jsx ,用高阶组件会省事点。
用自定义 hook 的一个很大的优点是代码很明确,就比如
const history = useHistory()
这样的代码你就知道 useHistory 返回的就是 history ,而用高阶组件时你可能要看看这个组件的文档或者代码实现你才知道它对原本的组件做了啥处理。从这个角度来讲,const [prompt, addEditing, removeEditing] = useLeavePrompt(offsetTop)
就会比 withLeavePrompt 好一些,如果你不看 withLeavePrompt 代码你不知道它会注入 addEditing 和 removeEditing 属性。这三者的一个对比可以见 Comparison: HOCs vs Render Props vs Hooks | Medium 。虽然这篇文章觉得万物皆可自定义 hook ,但是我觉得实际上还是要看场景来的,平时开发可以多试试这几种方式,从实战中感受下区别。
怎么去做抽象呢,可以把功能都先开发完,然后再想想哪些功能点是相对比较独立的,把比较独立的/需要复用的功能点给拆出去。当你比较熟悉这个过程后也可以选择在开发前期就先把抽象做好,就不用后面再拆了。
The text was updated successfully, but these errors were encountered: