前言
React的设计思想在很多地方体现了函数式编程的方式,各组件之间组合和复用的方式有多重,这里总结我日常开发中所用到的几种。
高阶组件
抛开React,作为JavaScript开发者,高阶函数应该是有很多接触,例如常见的防抖与节流(防抖与节流)。如果非要给他一个定义的话呢,我们还是看官方吧,高阶组件就是传入组件返回也是组件的函数 高阶组件
开发中经常会遇到渲染列表,若数据为空的时候显示为空的需求,如果是单个组件需要呢没关系,加个判断就好
render() {
if (_.isEmpty(this.prop.dataSource)) {
return <Empty />;
}
.....
}
如果是多个这样的判断的话呢,代码重复率就会很高,这个时候我们就可以考虑用高阶组件,将为空的判断抽离为一个高阶组件
const withEmpty = (WrappedComponent) => {
return (props) => {
return _.isEmpty(props.dataSource)
? <Empty />
: <WrappedComponent {...props} />;
}
}
使用
render() {
const WithEmptyUserList = withEmpty(UserList);
return <WithEmptyUserList dataSource={[]}/>;
}
相关部分官方给出了很多说明,可以参考高阶组件
render prop
在调用组件时,引入一个函数类型的 prop
,这个 prop
定义了组件的渲染方式,当然这个 prop
不是必须得叫 render
const Mouse = ({ render }) => {
const [x, setX] = useState(0);
const handleMouseMove = (e) => {
setX(e.pageX);
}
return <div onMouseMove={handleMouseMove}>
{render({ x })}
</div>
}
class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<Mouse render={({ x }) => <p>{x}</p>} />
)
}
}
Mouse
组件记录了移动的x坐标,渲染工作交由父组件传入的 render
函数。
Child Component
这在一些react公共组件里面很常见,比如 Context
、 react-router
// Context
render() {
return <Context.Consumer>
{(props) => {...}}
</Context.Consumer>
}
// React-router
render() {
return (
<HashRouter>
<Route path='/' render={() => '/'} />
<Route path='/home' render={() => '/home'} />
</HashRouter>
)
}
市面上有很多的公共组件都采用了类似的封装方式,还有比如 antd
的多个组件
这里实现了一个超懒版本的 react-router
,hashRouter
和 Route
的部分
const Context = createContext();
const HashRouter = ({ children }) => {
const [match, setMatch] = useState(_.replace(location.hash, '#', ''));
useEffect(() => {
const handleHashChange = (value) => {
setMatch(_.replace(location.hash, '#', ''));
}
window.addEventListener('hashchange', handleHashChange);
return () => window.removeEventListener('hashchange', handleHashChange);
}, []);
return <Context.Provider value={{ match }}>
{children}
</Context.Provider>
}
const Route = ({ path, render }) => {
return <Context.Consumer>
{({match}) => {
if (match === path) {
return render()
};
return null;
}}
</Context.Consumer>
}
class App extends Component {
constructor(props) {
super(props)
}
render() {
return (
<HashRouter>
<Route path='/' render={() => '/'} />
<Route path='/home' render={() => '/home'} />
</HashRouter>
)
}
}
关键两步
HashRouter
监听路由变化,保留hash
值,渲染所有的children
Route
中对比当前的hash
和传入的path
,如果正确则返回
真正的 react-router
相比这里多了很多判断,比如这里只是对 hash
值进行全等操作,实际上 Route
的判断要复杂得多,以下附上 Route
部分源码
{props.match
? children
? typeof children === "function"
? __DEV__
? evalChildrenDev(children, props, this.props.path)
: children(props)
: children
: component
? React.createElement(component, props)
: render
? render(props)
: null
: typeof children === "function"
? __DEV__
? evalChildrenDev(children, props, this.props.path)
: children(props)
: null}
推荐自行阅读相关源码
总结
文章的很多代码使用的 hook
所有16.8 以下的版本可能会有问题,以上总结基于本人日常工作中常用几种复用方式,欢迎讨论
🏆 掘金技术征文|双节特别篇