Learn from React 官方文档
一、Rendering Elements
1. Rendering an Element into the DOM
2. Updating the Rendered Element
React elements
是不可改变的,一旦创建,就不可以改变它的子节点或者属性。
上例每秒调用一次 tick()
函数,每次调用重新调用一次 ReactDOM.render()
,更新 React element
的内容。
React 只更新有必要更新的内容。 React DOM 把新的element和比较之前的element 比较,将不同的地方加以更新。例如打开浏览器控制台可以看到,在上例中只改变了 h2 的文本。
二、Components and Props
- 组件将 UI 分成独立的、可重用的小块。
- 组件和 JavaScript 函数相似。它们接受任意输入 (called "props"),返回描述所要显示的
React elements
。 - 组件的返回必须是一个单独的
element
而不可以是多个,所以可以用一个将其他元素包裹起来- 尽可能将一个组件分成多个更小的组件。
1. Functional and Class Components
- way 1: Functional Components
function Welcome (props) { return
Hello, {props.name}
; }接收参数:props 一个对象
返回:一个 React element- way 2: Class Components
class Welcome extends React.Component { render() { return
Hello, {this.props.name}
; } }2. Rendering a Component
- 当 React 遇到用户自定义的组件时,它给组件传递一个包含 JSX 属性的对象,这个对象是
props
。
{ name:"Sara" } //props 对象
上例的执行:
调用
ReactDOM.render()
,插入一个组件 React 调用
Welcome
组件,props
对象为 {name:"Sara"}Welcome组件输出
Hello Sara
React 更新 DOM 节点
组件的名字一定要大写字母开头
3. Composing Components
组件内使用可以用另一个组件:
4. Props are Read-Only
React 必须遵守的一条严格的准则:
所有的 React 组件必须是pure function
,因为props
是只读的pure function: 函数体的执行不修改传入的参数值
三、State and Lifecycle
-
state
可以动态地改变数据。它是私有的和完全由组件控制的。 - 使用
Class Component
定义的组件比Functional Component
多了一些特性,就是拥有 例如:state 。 - 生命周期钩子 (lifecycle hooks):声明在组件中,当组件
mount
和unmount
(刚植入和刚移除)时执行的方法. - The componentDidMount() hook runs after the component output has been rendered to the DOM.(
componentDidMount()
方法在组件的输出植入到 DOM 中的时候执行)。 -
this.props
和this.state
这些是在React.Component
中定义的,用户自定义的组件继承了React.Component
,所以不需要重新声明。 - 在拥有许多组件的应用中,当组件被移除时,释放组件已经占用的资源是非常重要的。
1. 时钟例子
//时钟例子:
- 上面的时钟代码的执行过程:
- 当
组件传递给ReactDOM.render()
, React 调用Clock
组件的constructor
,由于Clock
组件需要先显示当前的时间,在constructor
中初始化this.state
的值。 - React 接着调用
Clock
组件的render()
方法,Clock
组件render()
的输出返回要渲染的 DOM 元素。 - 当 DOM 元素插入后,React 调用生命周期钩子
componentDidMount()
。在这个方法中,Clock
组件生成一个计时器,要求浏览器每秒钟调用一次tick()
函数。 - 在
tick()
方法中调用this.setState()
方法,更新state
的值,更新 DOM 的内容。 - 当
Clock
组件从 DOM 中移除时,React调用componentWillUnmout()
方法,清除计时器。
2. 正确使用 state
- 不要使用赋值的方法直接修改
state
的值:
this.state.comment = 'hello'; //wrong
要使用 setState()方法更新state的值
this.setState({ comment:'hello'}); //correct
只有在constructor中可以直接为this.state分配值
constructor(props) { super(props); this.state = {comment:'hello'}; }
-
state
是异步更新的
由于this.props
和this.state
是异步更新的,所以不应该直接使用它们来设置新的state
值
this.setState({ counter: this.state.counter + this.props.increment, }); //wrong
使用接收一个函数作为参数的
setState()
:
第一个参数:prevState(state 的上一个值)
第二个参数:propsthis.setState((prevState, props) => ({ counter: prevState.counter + props.increment })); //correct
this.state
的值可以是一个对象,这个对象包含几个独立的属性,可以用多个setState()
独立的更新每一个属性单向数据流
一个组件可以将它的state
值作为props
传递给它的子组件而与外部的其他组件无关。
四、Handling Events
五、Conditional Rendering
1. example 1:
有两个组件:一个是
LoginGreeting
另一个是LogoutGreeting
;
在第三个组件Greeting
中,使用了这两个组件中的其中一个,根据Greeting
组件的props
对象的greet
属性值2. example 2: 使用元素变量
根据不同的
state
值,将不同的组件赋给一个元素变量,在另一个组件中使用变量名插入这个组件。3. example 3: 用短路与 && 操作符
只要当
&&
的左操作数为假时,表达式返回假,不计算右操作数;只有当左操作数为真时,才会计算右操作数的值,返回右操作数的值。4. example 4: condition? true : false
当
condition
的值为真时,返回冒号左边的表达式的值,当condition
的值为假时,返回冒号右边的表达式的值也可以返回更大的表达式,但是降低了可读性:
render() { const isLoggedIn = this.state.isLoggedIn; return (
{isLoggedIn ? ( //isLoggedIn 为 true 时返回 LogoutButton 组件); }) : ( //isLoggedIn 为 false 时返回 LoginButton 组件 )} 5. example 5: 使用返回null来隐藏一个组件
- 组件返回
null
不影响组件的生命周期方法,例如componentWillUpdate
和componentDidUpdate
依然可以被调用。
六、Lists and Keys
1. example 1:
使用
map()
函数遍历numbers
数组,每次遍历返回一个元素,将遍历后返回的结果数组赋给
listItems
,用一个
将结果数组包裹起来植入 DOM2. example 2:
组件接收一个数组,返回一个列表:
3. example 3:
当运行上面那些代码的时候,会在控制台得到一个
warning
,需要给列表元素增加key
属性4. example 4:
可以为数组元素对象设置一个ID属性 ,用来作为
key
如果没有ID属性作为
key
,也可以使用数组元素的索引作为key
:
(不推荐使用索引index
作为key
,因为当数组可以重排的时候,速度会变慢)5. example 5:
拆分组件的时候,要将
key
作为组件的属性,而不要把它放在组件返回的元素中
//wrong, 这样子相当于没有设置 key,控制台仍然会有关于 key 的 warning
//correct
6. example 6:
一个数组中每个数组元素的
key
必须互不相同,但是不同数组可以有相同的key
:七、 Forms
1. example 1: Controlled Component
的
value
值由 React 的state
控制:2. example 2: textarea
3. example 3: select
在 React 中通过给
标签设置
value
值来设置选择的选项;
的
value
值与的
value
值相同,则选中该;
这种方法更为方便,只要更新的
value
值即可改变选择的选项。4. example 4 :
当你需要处理多个
的时候,可以通过给每个
设置
name
属性,让事件处理函数根据event.target.name
的值来决定做什么事情[ name ]
: ES6 计算属性名this.setState({ [name]: value });
在 ES5 中相当于:
var partialState = {}; partialState[name] = value; this.setState(partialState);
八、Lifting State Up
经常在几个组件中,他们的数据受同一个变化的数据的影响,这时就要把
state
提到离这些组件最近的共同的父组件中。例如下面的例子中,输入摄氏度的组件
和输入华氏度的组件
他们的value
值相互影响,在摄氏度输入框输入的摄氏度值会转化为华氏度显示在华氏度的输入框,同样,输入的华氏度也会转化为摄氏度在摄氏度输入框显示。他们有共同的父组件
,将state
值设置在
中,根据state
值的scale
属性值判断是哪个输入框在输入,由此可将另一个输入框根据华氏度和摄氏度的转换公式进行转换。
组件根据摄氏度输入框的值显示不同的提示语,摄氏度值大于等于100,则显示:The water would boil.
摄氏度值小于100,则显示:The water would not boil.
九、Composition
1. example 1:
有一些组件一开始我们不知道他们的子节点是什么的时候,可以使用
{props.children}
在使用这个组件的时候再将子节点作为props
的一个属性传递给该组件。function Dialog(props) { return(
{props.children}); } function WelcomeDialog() { return( // Dialog 标签中间的 内容就作为Dialog组件的子节点 ); }2. example 2:
也可以使用自定义的属性名,在组件中多处插入不同的内容:
function SplitPane(props) { return(
{props.left}{props.right}和 是两个已经定义的组件 } right={ } /> ); } 3. example 3:Specialization
专门化:定义一个组件,在另一个组件中通过传入属性值,将该组件具体化为一个特殊的组件。
// 无内容的对话框组件轮廓 function Dialog(props) { return(
{props.title}
{props.message}
十、Thinking in React
- React 的目的是使用 JavaScript 快速地构建大型的 Web Apps。
- 使用 React 构建项目的流程如下例:
首先由你的 app 样式图开始,将你的 app 划分为由各个组件组成的一个大组件:
// 你的数据 [ {category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"}, {category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"}, {category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"}, {category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"}, {category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"}, {category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"} ];
Step 1: Break The UI Into A Component Hierarchy
- 把 UI 按层次划分成一个个组件:首先用一个个方框将你的 UI 划分成一个个组件,并给每个组件命名,根据一个组件只完成一件事的原则来划分组件。
- 明确每个组件的功能:
FilterableProductTable (orange)
包含了整个样例
SearchBar (blue)
接收所有用户输入,包括输入框的输入和复选框的勾选
ProductTable (green)
基于用户的输入显示和过滤数据集
ProductCategoryRow (turquoise)
显示每个分类的标题
ProductRow (red)
显示每一种产品的信息
这样就将整个样例如下划分了层次,包含在一个组件下的将作为它的子元素:
Step 2: Build A Static Version in React
- 用React建立静态的样式:这个过程使用
props
从父组件传递数据给子组件,而不要使用state
,state
是用来交互的,传递动态变化的数据的。 - 可以采用自顶向下也可以采用自底向上的顺序构建每一个组件。
Step 3: Identify The Minimal (but complete) Representation Of UI State
思考本例子中的每一个数据有:
原始的产品表单
用户的输入文本
复选框的值
过滤的产品表单
决定哪一项数据成为
state
思考的三个问题:它是通过父组件的
props
属性传递来的吗?如果是,它不是state
;它是否在一段时间内保持不变?如果是,它不是
state
;你是否可以通过其他
state
或者props
值计算出它的值?如果可以,它不是state
。在本例中,原始的产品表单是通过
props
传递的,不是state
;
用户输入的文本和复选框的值是动态的随着用户任意变化的,它是state
;
过滤的产品表单可以通过用户的输入和复选框的值计算显示,不是state
;
因此,本例的state
是用户输入的文本和复选框的值
Step 4: Identify Where Your State Should Live
明确state值放置的位置:
React 是单向数据流的;
每一个组件都根据state
植入某些内容;
每个组件有一个共同的父组件,这个组件拥有state
。本例中,过滤的产品表单
ProductTable
是根据state
值计算出来的,SearchBar
的文本框值和复选框值根据state
来显示,每个组件共同的父组件是FilterableProductTable
,因此FilterableProductTable
拥有state
:输入的过滤文本和复选框的值。
Step 5: Add Inverse Data Flow
最后本例的代码及最后的样式见我的Github。
十一、Typechecking With PropTypes
1. React.PropTypes
为属性设置类型:
- 在上面的例子中,只有当
name
属性的类型为string
时,才能正常显示; - 当
name=123
时,不符合设置的name属性类型,所以控制台报错;
2. Requiring Single Child
使用
React.PropTypes.element
可以要求传递给组件的子元素只能有一个:如果传入多个子元素:
控制台提示warning:
3. Default Prop Values
通过在
defaultProps
属性中设置默认的属性值,用于确保this.props.name
有值,如果父组件没有设置属性值的话。同样地,propTypes
也会检测defaultProps
中属性的类型。十二、Refs and the DOM
1. Adding a Ref to a DOM Element
-
ref
属性带有一个回调函数,这个回调函数在组件mounted
或者unmounted
的时候执行; - 当
ref
用在html
元素时,ref
的回调函数接收拥有这个ref
的 DOM 元素作为它的参数;
- 可以把
ref
添加到用class
定义的组件上 - 不可以把
ref
添加到用function
定义的组件上 - 可以在一个用
function
定义的组件内为一个用class
定义的组件添加ref