hook

render Props 和 HOC

在 hook 之前我们可以使用 render-props 和 higher-order components 来进行状态共享

render Props

  • 具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑,render prop 是一个用于告知组件需要渲染什么内容的函数 prop

  • 即使用了render这个prop的组件,接受一个函数来渲染元素,即父组件控制这部分的显示内容,而不是组件内部去实现自己的渲染逻辑。而父组件渲染的元素使用的数据是子组件内部控制的状态

考虑如下场景:后台数据列表 = 搜索 + 展示table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// index.jsx
class ListIndex extends React.PureComponent {
render() {
return (
<div>
<Breadcrumb />
<SearchWithList />
</div>
)
}
}

// search.jsx
class SearchWithList extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
res: [1, 2]
}
}
render() {
return <div>
<List sourceData={this.state.res} />
</div>
}
}

// list.jsx
class List extends React.PureComponent {
render() {
const { sourceData } = this.props
return (
<div>
{sourceData.map(n => {
return <div key={n}>{n}</div>
})}
</div>
)
}
}

上面代码没任何问题,但是有弊端,将 list 整合到 search 里,只能处理特定场景,不具备通用性,如果遇到搜索条件相通,但是列表完全不同的情况无法复用

基于 render prop 的方式处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// index.jsx
class ListIndex extends React.PureComponent {
render() {
return (
<div>
<Search getPropFun={prop => <List sourceData={prop} />} />
</div>
)
}
}

// search.jsx
class Search extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
res: [1, 2]
}
}

render() {
return <div>
{ this.props.getPropFun(this.state.res) }
</div>
}
}

// list.jsx
class List extends React.PureComponent {
render() {
const { sourceData } = this.props
return (
<div>
{sourceData.map(n => {
return <div key={n}>{n}</div>
})}
</div>
)
}
}
  • 相较于第一种方式,使用 render prop 相当于约定了 search 组建的输出格式,无关你的 list 何种展示形式,只要适配我的数据结构即可,将 list 作为 getPropFun 属性的值,并在 search 内容渲染

  • 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”

使用 Props 而非 render

可以简单地使用 children prop

1
2
3
<Mouse children={data => (
<p>这是内容{data}</p>
)}/>

children prop 并不真正需要添加到 JSX 元素的 “attributes” 列表中。你可以直接放置到元素的内部

1
2
3
4
5
<Mouse>
{data => (
<p>这是内容{data}</p>
)}
</Mouse>

HOC

定义:一个接收一个组件作为参数并返回一个经过改造的新组件,通过一个函数,对某些组件进行数据加工或权限认证

简单的HOC

1
2
3
4
5
6
7
function control(wrappedComponent) {
return class Control extends React.PureComponent {
render(){
return <wrappedComponent {...this.props, message: 'hello world'} />
}
}
}

一个loading

1
2
3
4
5
6
7
8
9
10
function loading(wrappedComponent) {
return class Loading extends React.PureComponent {
render(){
if(!this.props.data) {
return <div>loading...</div>
}
return <wrappedComponent {...props} />
}
}
}

hook

动机:

  • 在组件之间复用状态逻辑很难(需要render props 和 高阶组件解决)

  • 复杂组件变得难以理解,逐渐会被状态逻辑和副作用充斥

类组件缺点:

  • 大型组件很难拆分和重构,也很难测试

  • 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑

  • 组件类引入了复杂的编程模式,比如 render props 和高阶组件

注意事项:

  • 只能在函数内部的最外层调用 Hook,不要在循环、条件判断或者子函数中调用

  • 只能在 React 的函数组件中调用 Hook,不要在其他 JavaScript 函数中调用

useState

useState 工作:

  • 通过一些途径产生更新,更新会造成组件render

  • 组件render时useState返回的num为更新后的结果

基本使用:

1
2
3
4
function App() {
const [num, updateNum] = useState(0);
return <p onClick={() => updateNum(num => num + 1)}>{num}</p>;
}

分析其使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function useState(initialState) {
// 当前useState使用的hook会被赋值该该变量
let hook;

if (isMount) {
// ...mount时需要生成hook对象
} else {
// ...update时从workInProgressHook中取出该useState对应的hook
}

let baseState = hook.memoizedState;
if (hook.queue.pending) {
// ...根据queue.pending中保存的update更新state
}
hook.memoizedState = baseState;

return [baseState, dispatchAction.bind(null, hook.queue)];
}
返回
顶部