React 本教程描述一些基本概念
如果需要入门学习,推荐教程: https://reactjs.org/
本教程为了加强记忆,更改了学习的顺序。 且会收集教程以外实例。
React实现了与浏览器无关的DOM系统,以实现性能和跨浏览器的兼容性。
0. 知识
0.1,ES6 箭头函数 :
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
// 实例应用
const materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
巧妙的完成了数组长度的调用;
console.log(materials.map(material => material.length));
// expected output: Array [8, 6, 7, 9]
- params => ({foo: bar}) 加括号的函数体返回对象字面量表达式:
- (param1, param2, ...rest) => { statements } 【and】(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements } 支持剩余参数和默认参数。如例(剩余参数):
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
- () => { statements } 没有参数的函数应该写成一对圆括号。
- singleParam => { statements } 当只有一个参数时,圆括号是可选的。
- 同样支持参数列表解构
什么是解构?解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// expected output: 10
console.log(b);
// expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// expected output: Array [30,40,50]
// ---
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();
// expected output: 6
0.2,进阶指南
- 无障碍
- 代码分割
- Context
- 错误边界
- Refs 转发
- Fragments
- 高阶组件
- 与第三方库协同
- 深入 JSX
- 性能优化
- Portals
- Profiler
- 不使用 ES6
- 不使用 JSX
- 协调
- Refs & DOM
- Render Props
- 静态类型检查
- 严格模式
- 使用 PropTypes 类型检查
- 非受控组件
- Web Components
相关知识:
- ES6 语法:教程
- Babel:教程
- React:教程,示例库
- Webpack:教程
- React 项目脚手架:代码库
- Flex 布局:教程,示例
- CSS Modules:教程,示例库
- React-Router:教程,示例库
- Flux 架构:教程,示例库
- Redux 架构:教程一、教程二、教程三
- Mocha 测试框架:教程,示例库
- Istanbul 覆盖率框架:教程
- React 单元测试:教程,示例库
1. Component API && Component
- react versions 使用前明确环境与使用的版本。
- 组件类 API参考:
Props
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
function HelloMessage(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="Runoob"/>;
ReactDOM.render(
element,
document.getElementById('example')
);
关于组件生命周期
每个组件都有几个“生命周期方法”,您可以重写它们以在流程中的特定时间运行代码。可以参考的生命周期图。
- 安装时
- static getDerivedStateFromProps() ; 在初始安装和后续更新上,调用render方法之前立即调用。
- constructor()
通常,在React中,constructor构造函数仅用于两个目的:
1.通过将对象分配给来初始化本地状态this.state。
2.将事件处理程序方法绑定到实例。 - render()
该render()方法是类组件中唯一需要的方法。
调用时,它应该检查this.props并this.state返回以下类型之一:
- React elements 元素
- Arrays and fragments.。从渲染返回多个元素。
- Portals. 将子级渲染到另一个DOM子树中。
- String and numbers 字符串和数字。
- Booleans or null 布尔值或null.
该render()函数应该是纯函数,这意味着它不会修改组件状态,它在每次调用时都返回相同的结果,并且不与浏览器直接交互。
如果您需要与浏览器进行交互,请改为使用componentDidMount()或其他生命周期方法执行工作。保持render()纯净使组件更容易考虑。 - componentDidMount() ; 挂载组件(插入树中)后立即调用。需要DOM节点的初始化应在此处进行。
- 更新时
- static getDerivedStateFromProps()
- shouldComponentUpdate(nextProps, nextState); 当接收到新的Props或State 时,在渲染之前调用。默认为true。
- render()
- componentDidUpdate(prevProps, prevState, snapshot); 更新发生后立即调用。初始渲染不调用此方法。
- getSnapshotBeforeUpdate() - 卸载时
- componentWillUnmount() - 错误处理
可在其子组件树中的任何位置捕获JavaScript错误,记录这些错误并显示后备UI,而不是崩溃的组件树。错误边界会在渲染过程中,生命周期方法中及其下的整个树的构造函数中捕获错误。
- static getDerivedStateFromError()
后代组件引发错误后,将调用此生命周期。它接收作为参数抛出的错误,并且应返回值以更新状态。
- componentDidCatch()
后代组件引发错误后,将调用此生命周期。它接收两个参数:
- error -引发的错误。
- info-具有componentStack密钥的对象,其中包含有关哪个组件引发了错误的信息。
componentDidCatch()在“提交”阶段调用,因此允许出现副作用。它应用于记录错误之类的事情: -
其它API :
- setState() - forceUpdate()
- 类属性
- defaultProps
- displayName - 实例属性
- props
- state
2. 元素渲染
<div id="root"></div>
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
它在页面上显示“ Hello,world”。
3. JSX
import React from 'react';
import React from 'react';
// Correct! This is a component and should be capitalized:
function Hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
// Correct! React knows <Hello /> is a component because it's capitalized.
return <Hello toWhat="World" />;
}
与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。
要将 React 元素渲染到根 DOM 节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:
const element = < HelloWorld />;
ReactDOM.render(element, document.getElementById('example'));
4,This && 事件处理 Handling Events
必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
HTML
<button onclick="activateLasers()">
Activate Lasers
</button>
在React中略有不同:
<button onClick={activateLasers}>
Activate Lasers
</button>
使用React时,通常不需要addEventListener在创建DOM元素后调用将侦听器添加到DOM元素。相反,仅在最初呈现元素时提供一个侦听器。
此Toggle组件呈现一个按钮,该按钮使用户可以在“ ON”和“ OFF”状态之间切换:
class Toggle extends React.Component {
constructor(props)
{
//子类 必须 在 constructor( )调用 super( )方法,否则新建实例时会报错。
// 报错的原因是:子类是没有自己的 this 对象的,它只能继承自父类的 this 对象,然后对其进行加工,而super( )就是将父类中的this对象继承给子类的。没有 super,子类就得不到 this 对象。
//无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的;
super(props);
this.state = {isToggleOn: true};
// 这种绑定是使`this`在回调中起作用所必需的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({ isToggleOn: !state.isToggleOn }));
}
render() {
return (
<button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
5.React 条件渲染 && React 列表 & Keys
条件
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
列表
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
6. Hook( 挂钩 )&& React State(状态)
简介
Hook 是React 16.8中的新增功能。它们使您无需编写类即可使用 State 和其他React功能。
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Hooks 钩子 解决了React在我们看来的各种的问题。无论您是学习React,每天使用它,还是偏爱具有相似组件模型的其他库,您都可能会意识到以下的一些问题。
- 很难在组件之间重用状态逻辑
- 复杂的组件变得难以理解
- 类会使人和机器混淆
至关重要的是,Hooks与现有代码并存,因此您可以逐渐采用它们。
如果想探索更多Hook的动机,可以详细参考(RFC)。
钩子一览
建立自己的挂钩
有时,我们想在组件之间重用一些状态逻辑。传统上,有两种流行的解决方案:Higher-Order Components 和 Render Props。使用自定义钩子可以执行此操作,但无需在树中添加更多组件
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
// 它friendID 作为参数,并返回我们的朋友是否在线。现在我们可以在两个组件中使用它:
//
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
//
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
这些组件的状态是完全独立的。挂钩是重用有状态逻辑而不是状态本身的一种方法。实际上,每个对Hook的调用都具有完全隔离的状态-因此,您甚至可以在一个组件中使用同一自定义Hook两次。
自定义钩子更多的是约定俗成的功能。如果函数的名称以“ use” 开头并且调用了其他挂钩,则我们将其称为自定义挂钩。该useSomething命名约定是我们的棉短绒插件如何能够找到使用钩在代码中的bug。
您可以编写自定义的Hook,这些自定义的Hook涵盖了各种用例,例如表单处理,动画,声明式订阅,计时器,以及可能还没有考虑的更多用例。
State Hook
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
// 等效类的示例
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Effect Hook
如果你熟悉阵营类生命周期方法,你能想到的useEffect钩。因为componentDidMount,componentDidUpdate和componentWillUnmount结合。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`; });
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
钩子规则:挂钩是JavaScript函数,但是它们施加了两个附加规则
- 仅在顶层调用Hooks。
不要在循环,条件或嵌套函数中调用Hook。请始终在您的React函数的顶层使用挂钩。通过遵循此规则,可以确保每次渲染组件时都以相同的顺序调用Hook。
- 仅从React函数组件调用Hook 。
不要从常规的JavaScript函数中调用Hook。React从React函数组件中调用Hook。可以从自定义挂钩中调用挂钩 。
我们提供了一个linter插件来自动执行这些规则。我们知道这些规则乍看起来似乎是局限性的或令人困惑的,但是它们对于使Hooks正常工作至关重要。