内容简介:本文是『horseshoe·React专题』系列文章之一,后续会有更多专题推出来我的来我的个人博客 获得无与伦比的阅读体验
本文是『horseshoe·React专题』系列文章之一,后续会有更多专题推出
来我的 GitHub repo 阅读完整的专题文章
来我的个人博客 获得无与伦比的阅读体验
React是用来解决状态同步的,但它却有一个与 this.state
并驾齐驱的概念。
这就是 this.props
。
this.props
是组件之间沟通的一个接口。
原则上来讲,它只能从父组件流向子组件,但是开发者有各种hack技巧,基本上近亲之间沟通是不成问题的。
this.props
this.props
是一个极其简单的接口。世界需要更多这样的傻瓜接口
你只需要像写HTML标签的属性一样,把它写上去,它就传到了子组件的 this.props
里面。
不过有几个地方需要注意:
-
有两个特殊的属性
ref
和key
,它们各有用途,并不会传给子组件的this.props
。 -
如果只给属性不给值,React会默认解析成布尔值
true
。 - 除了字符串,其他值都要用花括号包裹。
- 如果你把属性给了标签而不是子组件,React并不会解析。
import React, { Component, createRef } from 'react'; import Child from './Child'; class App extends Component { isPopular = false; refNode = createRef(); render() { return [ <Child key="react" ref={this.refNode} isPopular />, <Child key="vue" url="https://github.com/vuejs/vue" star={96500} />, <Child key="angular" owner="google" isPopular={this.isPopular} />, ]; } } export default App; 复制代码
this.props
是一个不可变对象
React具有浓重的函数式编程的思想。
提到函数式编程就要提一个概念:纯函数。
纯函数有几个特点:
- 给定相同的输入,总是返回相同的输出。
- 过程没有副作用。
- 不依赖外部状态。
function doSomething(a, b) { return a + b; } 复制代码
这是一种编程思想。如果你对这个概念有点模糊,我可以举个例子:
你的杀父仇人十年后突然现身,于是你决定雇佣一个冷面杀手去解决他。
你会找一个什么样的杀手呢?
- 给多少钱办多少事,效果可预期,从不失手。
- 不误伤百姓,不引起动静。
- 没有团伙,单独作案,干净利落,便于封口。
如果你面对杀父仇人有这样的觉悟,那么纯函数便是你的囊中之物了。
为什么要提纯函数?因为 this.props
就是汲取了纯函数的思想。
它最大的特点就是不可变。
跟 this.state
不一样的是, this.props
来真的。虽然 this.state
也反对开发者直接改变它的属性,但毕竟只是嘴上说说,还是要靠开发者自己的约束。然而 this.props
会直接让你的程序崩溃。
加上React也没有 this.setProps
方法,所以不需要开发者自我约束, this.props
就是不可变的。
沟通基本靠吼
父组件给子组件传值
这个无需赘言,最直观的传值方式。
import React from 'react'; import Child from './Child'; const App = () => { return ( <Child star={1000} /> ); } export default App; 复制代码
子组件给父组件传值
其实就是利用回调函数的参数传递值。
父组件定义一个方法,将该方法通过props传给子组件,子组件需要给父组件传值时,便传参执行该方法。由于方法定义在父组件里,父组件可以接收到该值。
import React, { Component } from 'react'; import Child from './Child'; class App extends Component { state = { value: '' }; render() { return ( <Child handleSomething={this.handleSomething} /> ); } handleSomething = (e) => { this.setState({ value: e.target.value }); } } export default App; 复制代码
import React from 'react'; const Child = (props) => { return ( <input type="text" onChange={props.handleSomething} /> ); } export default Child; 复制代码
兄弟组件之间传值
原理和回调函数一样,只不过这里父组件只是一个桥梁。
父组件接收到回调函数的值以后,通过 this.setState
保存该值,并触发另一个子组件重新渲染,重新渲染后另一个子组件便可以获得该值。
import React, { Component, Fragment } from 'react'; import ChildA from './ChildA'; import ChildB from './ChildB'; class App extends Component { state = { value: '' }; render() { return ( <Fragment> <ChildA handleSomething={this.handleSomething} /> <ChildA value={this.state.value} /> </Fragment> ); } handleSomething = (e) => { this.setState({ value: e.target.value }); } } export default App; 复制代码
import React from 'react'; const ChildA = (props) => { return ( <input type="text" onChange={props.handleSomething} /> ); } export default ChildA; 复制代码
import React from 'react'; const ChildB = (props) => { return ( <div>{props.value}</div> ); } export default ChildB; 复制代码
createContext
:alien:这是React v16.3.0发布的API。
React为开发者提供了一扇传送门,它就是Context对象。
严格来说,Context早就存在于React中了,不过一直以来都不是正式的API。
终于在v16.3.0转正了。
为什么说Context是一扇传送门?因为它可以跨组件传递数据。不是父子之间的小打小闹哦,而是可以跨任意层级。但是有一个限制,数据只能向下传递,原因就是后面要讲到的单向数据流。
开发者通过 createContext
创建一个上下文对象(React特别喜欢create),然后找一个顶级组件作为 Provider
。接下来就可以在任意下级组件消费它提供的数据了。
-
只要
Provider
的数据改变,就会触发Consumer
的更新。 -
创建时可以提供一个默认值,另外挂载时可以通过value属性传递数据。但是默认值只有在不提供
Provider
的情况下才起作用。 - 开发者可以创建多个Context。
-
Consumer
的children必须是一个函数。
旧的Context存在一个问题,如果接收组件的 shouldComponentUpdate
生命周期钩子返回false,则它不会接收到Context中的数据,因为它是通过 this.props
一级一级往下传的。
而新的Context采取的是订阅发布模式,所以不存在这个问题。
实际上react-redux库的 Provider
组件内部就是使用了旧的Context API,不过redux做了一些优化。
import { createContext } from 'react'; const { Provider, Consumer } = createContext({ lang: 'en' }); export { Provider, Consumer }; 复制代码
import React, { Component } from 'react'; import { Provider } from './context'; import Todos from './Todos'; const App = () => { return ( <Provider value={{ lang: 'zh' }}> <Todos /> </Provider> ); } export default App; 复制代码
import React, { Fragment } from 'react'; import TodoItem from './TodoItem'; const Todos = () => { return ( <Fragment> <TodoItem /> <TodoItem /> <TodoItem /> </Fragment> ); } export default Todos; 复制代码
import React from 'react'; import { Consumer } from './context'; const TodoItem = () => { return ( <Consumer> {({ lang }) => <div>{lang === 'en' ? 'todo' : '要做'}</div>} </Consumer> ); } export default TodoItem; 复制代码
单向数据流
水往低处流,这是自然规律。
React通过描述状态来控制UI的表达,这就涉及到UI的更新机制。
状态除了内部状态之外,肯定有一些状态是要组件之间共享的,所以,一旦一个组件的状态更新了,可能会牵扯到很多组件的更新,框架的更新机制必将变的异常复杂。
但是回归到水的意象,如果状态的流向是单向的,而且是自上往下流动,这就变的非常符合直觉,而且更新机制可以做到极简:我更新,则我的所有下级也更新。
这就是 this.props
的思想源头。
它虽然叫props,但它也是状态,只不过是共享的状态。
它只能自顶向下流动。
内部不能改变 this.props
。
某个props的源头更新了,则流经的所有组件都要更新,除非开发者手动禁止。
脉络清晰, this.props
才是赋予了React血液的东西。
关于React摒弃了表单双向数据绑定的问题,它只是想把单向数据流做的更彻底一点。其实表单的状态,归根结底是组件内部的状态,跟单向数据流无关。
什么是双向数据绑定?就是表单输入,与之绑定的变量自动获取到输入的值,变量的值改变,与之绑定的表单的值随即改变,两种流向都自动绑定了。
但其实双向数据绑定不就是value的单向绑定加onChange事件监听么!React也可以通过两步做到。
总结:双向数据绑定不影响单向数据流,React也可以实现双向的同步。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。