浅析组件化时代的前端状态管理(二):原生的方式

使用原生的方式管理状态

第一类所提到的几种方式是最基础的组件通信的方式, 可以通过如下的一个最简单的例子,

数据存放在父组件:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import React, { PureComponent, Component } from "react";
import ReactDOM from "react-dom";

// 需求: 写一个表单信息预览卡片, 需要实现 左侧展示表单, 右侧实时展示表单的填写数据预览
// 实现: 把 App拆成两个组件, 分别为表单组件和 表单预览组件, 组件的状态数据全部存放在App组件中, 通过 props 传递给 子组件, 子组件通过this.props.onChange 修改父组件的 state数据

// 表单 输入框组件
class FormInput extends PureComponent {
render() {
return (
<>
输入:
<input onChange={this.props.onChange} value={this.props.value} />
</>
// 更多其他的输入框
);
}
}

// 表单预览组件
class FormPreview extends PureComponent {
render() {
return (
<label>预览: {this.props.value}</label>
// 更多其他的渲染逻辑
);
}
}

class App extends Component {
constructor() {
super();
this.state = {
value: ""
};
this.onChange = this.onChange.bind(this);
}
onChange = event => {
this.setState({
value: event.target.value
});
};

render() {
return (
<div>
demo_2_1:
<FormInput value={this.state.value} onChange={this.onChange} />
<hr />
<FormPreview value={this.state.value} />
</div>
);
}
}

export default App;

Edit front_end_state_manage_demo

使用ContextAPI实现:

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

import React, { PureComponent, Component } from "react";

// 使用ContextAPI的方式. 如何了理解 contextAPI? 可以理解contexAPI就是一个桥梁, 可以实现props的夸组件层级传递, 比如可以把props从跟节点传递到底层的节点
// 关于ContextAPI详情可以参考 https://reactjs.org/docs/context.html#reactcreatecontext

const FormDataContext = React.createContext({});

// 输入框组件
const Input = () => {
return (
<FormDataContext.Consumer>
{props => {
return (
<>
输入:
<input onChange={props.onChange} value={props.value} />
</>
);
}}
</FormDataContext.Consumer>
);
};

// 表单容器
const FormContainer = () => {
return (
<Input />
// ... 其他表单字段
);
};

// 预览组件
const PreviewDetail = (props = {}) => {
return (
<FormDataContext.Consumer>
{props => {
return <label>预览:{props.value}</label>;
}}
</FormDataContext.Consumer>
);
};

// 预览容器
const PreviewContainer = () => {
return (
<PreviewDetail />
// ...其他预览详情内容
);
};

class App extends Component {
constructor() {
super();
this.onChange = this.onChange.bind(this);
this.state = {
value: "",
onChange: this.onChange
};
}
onChange = event => {
this.setState({
value: event.target.value
});
};
render() {
return (
<FormDataContext.Provider value={this.state}>
<FormContainer />
<hr/>
<PreviewContainer />
</FormDataContext.Provider>
);
}
}

export default App;

Edit front_end_state_manage_demo

从上面ContextAPI的例子可以看到, 我们不需要一层层的传递props到底层的节点, 只需要最外层使用Provider包裹一下, 然后再需要使用数据的底层组件使用 Consumer包裹一下, 数据就能实现从顶层到底层的跨层级传递, redux里面connect实现的原理也是如此, 后续会详细介绍.

自定义事件

没有嵌套关系组件之间的通信可以通过自定义事件来进行通信

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import React, { PureComponent, Component } from "react";

class Child_1 extends Component {
constructor(props) {
super(props);
this.state = {
msg: "hello"
};
}

componentDidMount() {
setTimeout(() => {
// 发布 msg 事件
document.dispatchEvent(new CustomEvent("msg", { detail: "world" }));
}, 1000);
}
render() {
return (
<div>
<p>child_1 component: {this.state.msg}</p>
</div>
);
}
}

class Child_2 extends Component {
constructor(props) {
super(props);
this.state = {
msg: ""
};
}

componentDidMount() {
// 监听 msg 事件
document.addEventListener(
"msg",
e => {
this.setState({
msg: e.detail
});
},
false
);
}

render() {
return (
<div>
<p>child_2 component: {this.state.msg}</p>
</div>
);
}
}

class App extends Component {
render() {
return (
<div>
demo_2_3:
<Child_1 />
<Child_2 />
</div>
);
}
}
export default App;

Edit front_end_state_manage_demo

组件A如果依赖组件B的状态, 可以监听组件B的状态变化事件,进而获取数据, 这种方式本质上是一种观察者模式. 和状态数据共享其实是两回事, 最终达到的目的是一样的. 在这一理念甚至已经衍生出相关的框架了 cyclejs