React 组件中的数据流动方向是 自上而下 的,也就是说在组件树中,数据只能从父组件以属性的方式传递到子组件
父组件的数据可能是其接收到的属性,也可能是自身的内部状态
解释:
React 是单项数据流
函数属性是子组件用来通知父组件发生了什么,它更像是子组件触发的一个事件
父组件可以根据业务逻辑来选择如何处理这个事件,它可以更新数据后重新传递给子组件,也可以不予理会
双向数据绑定
在双向数据绑定中,父组件将数据传递给子组件,子组件修改数据后会将数据回传同步给父组件
父组件是无条件接受的
状态提升
状态提升的意思是,当组件A需要依赖另外一个组件B的内部状态
而他们又不是父子关系时,需要将组件B的内部 状态提升 到他们公共的祖先组件中管理,就可以通过属性接收到这份数据
当组件B需要对数据进行变更时,可以通过函数属性来通知祖先组件对数据更新,然后重新传递给子组件
唯一数据来源
数据唯一来源是官方推荐的数据共享原则,也是最符合 React 设计理念,与单项数据流特性相辅相成,务必遵守
解释:
为什么多个组件之间必须共用同一份数据,是否可以引入一个事件库,一个组件分发事件,另一个组件注册相应的事件来接收数据自己维护
原因:
破坏了组件的封装性,易于复用的组件都是相对独立的,它只需要定义自己需要的数据和行为(函数属性)即可,不需要谁帮助分发事件
数据传递不连续,会增加项目的复杂性,当项目到一定阶段后,对这份数据的依赖会变得难以维护
相同的数据会有多个副本,需要保证数据同步,在增加项目的复杂性的同时也提高了出现BUG的几率
Redux
React 是一个状态管理库,他不是专属于 React 的,但是和 React 配合很不错
在前端应用规模比较小的时候,可以不使用任何状态管理工具,只需要使用状态提升的方式来管理应用状态即可
为了让应用状态更直观,可以将组件作为状态总线,来管理整个应用所有的状态,对于小规模的项目是推荐的
但是当前端应用规模比较复杂时,就需要类似 Redux 来专门进行状态管理,作用如下:
维护一个数据仓库(store)管理整个应用的状态(state),确保数据的唯一来源
可以通过 dispatch 方法分发一个 action ,来通知 Redux 需要对数据进行变更
Redux 接收到 action 后可以依据 action 的类型对 state 进行相应的修改
数据更新后 Redux 会触发注册的监听器(如:更新组件属性),完成视图更新
类似的状态管理工具还有 MobxJS(https://mobx.js.org/),可了解一下
组件通信——context
React 官方不推荐使用的特性,只是实验性的API,所以暂不介绍
组件通信——ref
每一个 React 组件都有一个特殊的属性 ref ,该属性的值可以是一个字符串,也可以是一个函数。
由于字符串形式的ref 在内部实现和实际使用中存在诸多问题,官方不推荐使用,而且可能在会被移除
所以只需要记住在看到字符串形式的ref 属性时知道有这种方法就可以
当 ref 属性值是一个函数时
如果组件是一个 HTML 元素兼容的 React 内部组件时(如:div、img等),函数接收其对应的原生DOM节点作为参数
如果组件是一个以类的方式定义的组件时,函数接收该组件类的实例作为参数
如果组件是一函数方式定义的组件,那么设置为ref 值的函数永远接收到的都是 null
ref 与组件通信之间的关系:
示例:
class UserForm extends React.Component{
constructor(props){
super(props)
this.state={
name:null,
age:null
}
}
formData(){
return this.state
}
handleFiledChange(e){
const {name,value}=e.target
this.setState({
[name]:value
})
}
render(){
return (
<div>
<input type="text" name="name" placeholder="Name" onChange={e=>this.handleFiledChange(e)}>
<input type="text" name="age" placeholder="Age" onChange={e=>this.handleFiledChange(e)}>
</div>
)
}
}
class App extends React.Component{
handleSubmit(){
const formData=this.form.formData()
alert(`formData:${JSON.stringify(formData)}`)
}
render(){
return (
<div>
<UserForm ref={form=>this.form=form} />
<button onClick={()=>this.handleSubmit()}>Submit<button>
</div>
)
}
}
ReactDOM.render(<App/>,document.querySelector("#root"))
示例结果:
既然通过ref 能获取子组件的实例,所以也可以调用其他成员方法,从而获取数据
但是这不是一个好的方式,因为作为一个组件,是需要有一定的封装性的
它应该对外只会承诺我接受什么样的属性,但不会承诺有什么样的成员方法
也就是说,如果 JavaScript 的类支持私有成员方法,那么React组件类中的成员方法都应该定义成私有的
ref 有自己的使用场景,此处只是说明这种方式不适用于组件间通信
总结:使用状态提升的方式在多个组件之间共享数据,切记维持应用单向数据流和数据唯一来源原则
文件借鉴: http://react-china.org/t/react-react/15548