react的入门学习笔记(就是照抄官方文档)
学习路径
核心概念
起步实践
用Nx建立第一个react项目
封装CSS3-based的烟花组件 (学习写css样式的方法、
JSX
、组件传参Props
)行内/声明样式:样式作为js对象传入组件的style属性
import样式
使用*.module.css防止样式全局污染,使用
className={moduleName.className}
以引用styled components:从组件的层面对 CSS 进行封装
其他核心概念
state
函数组件转换成class组件
- 创建一个同名的 ES6 class,并且继承于
React.Component
。 - 添加一个空的
render()
方法。 - 将函数体移动到
render()
方法之中。 - 在
render()
方法中使用this.props
替换props
。 - 删除剩余的空函数声明。
- 创建一个同名的 ES6 class,并且继承于
向class组件添加局部的state
添加一个class构造函数,在函数中为
this.state
赋初值。同时将props
传递到父类的构造函数1
2
3
4constructor(props) {
super(props);
this.state = {date: new Date()};
}使用
this.state.name
调用,使用this.setState()
对整个state对象进行更新不要直接修改
State
,请使用this.setState
方法。构造函数是唯一可以给this.state
赋值的地方因为
this.props
和this.state
可能会异步更新,不要依赖他们的值来更新下一个状态,可以通过使this.setState()
接受一个函数来解决异步的问题1
2
3
4
5
6
7
8
9
10
11
12
13
14// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
})
事件处理
回调函数必须进行绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 方案一 构造器绑定
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
// 方案二 public class fields 语法
handleClick = () => {
console.log('this is:', this);
}
// 方案三 调用时使用箭头函数(当回调函数作为prop传入子组件时此方案会产生性能问题)
render() {
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}向事件处理函数传递参数
1
2<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>子组件向父组件通信:在父组件向子组件将某个事件处理函数作为props传入,子组件调用
条件渲染
- 使用变量存储元素
- 与运算符
&&
true && expression
总是会返回expression
false && expression
总是会返回false
- 三目运算符
列表和key
- 不需要改变列表项目顺序时,才能使用
index
作为列表key:列表中的key有什么用:知乎 - 元素的key只有放在就近的数组上下文中才有意义:
map()
中的元素需要设置key属性 - key值仅仅对React有效,若程序需要获取该值则需要使用其他的属性名传入
表单
表单元素(如
<input>
、<textarea>
和<select>
)之类的表单元素自己维护 state,并根据用户输入进行更新,它们原本是非受控组件。在React可以通过使它们接受value
属性,编写onChange()
事件方法使它们成为受控组件(受控组件:输入的值由React的state驱动)受控组件
- 文件input标签
1
<input type="file" />
- 处理多个输入:给每个元素添加
name
属性,并让处理函数根据event.target.name
的值选择要执行的操作 - 受控组件上value属性值接受到
undefined
或null
时会重新成为非受控组件,并且终端会提示
状态提升
- 通过状态提升的例子理解以上React中的核心概念,实现父子组件传值、兄弟组件通讯
使用组合代替继承
包含关系
特殊的
children
prop:组件标签中的所有内容都会作为children
prop使用
props
传入组件并在展示在预留的位置:类似slot
的概念,但是React任何东西都是作为props
传递
特例关系:“特殊”组件可以通过 props 定制并渲染“一般”组件:
理解hook
React ^16.8
允许在不编写class的情况下使用state等react属性,Hook开发的动机:
组件间共享状态逻辑
在组件之间重用一些状态逻辑。目前为止,有两种主流方案来解决这个问题:高阶组件和 render props。自定义 Hook 可以让你在不增加组件的情况下达到同样的目的。
拆分复杂组件内相互关联的部分
函数组件替代Class组件
hook的使用规则:
- 只能在函数最外层调用
- 只能在函数组件调用
内置HooK
useState
操作组件state:调用useState()
添加state。useState()
返回当前状态(this.state.name
)和一个更新该状态的函数(this.setState()
),参数为state的初始值
1 |
|
在state hook中更新一个 state 变量,会 替换 它的值。这和 class 中的
this.setState
不一样,后者会把更新后的字段 合并 入对象中。因此,建议降低组件中state对象的拆分粒度
useEffect
操作组件的生命周期:它跟 class 组件中的
componentDidMount
、componentDidUpdate
和componentWillUnmount
具有相同的用途,只不过被合并成了一个 API。useEffect
在执行DOM更新后被调用1
2
3
4
5
6
7
8useEffect(() => {
// 相当于 componentDidMount 和 componentDidUpdate:
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
// 相当于 componentWillUnmount:
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});大多数情况下,Effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的
useLayoutEffect
Hook 供你使用,其 API 与useEffect
相同。清除effect:在Effect返回一个清除函数,React会在组件卸载的时候执行清除操作。
1
2
3
4
5
6
7
8
9
10useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});跳过Effect进行性能优化:如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为
useEffect
的第二个可选参数即可:1
2
3useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
提示:
- Hook的目的:实现关注点分离(单个操作逻辑完整性)。按照代码的用途分离多个effect
- 每次更新都会运行hook,除非指定跳过
Hook规则
- 只在最顶层使用,不再循环、条件或嵌套函数中调用Hook。因为React依赖Hook调用顺序确定state对应的useState,Hook 的调用顺序在每次渲染中相同才能保证正确性。
- 只在React函数中调用Hook
- 在React函数组件中调用Hook
- 在自定义Hook中调用其他Hook
自定义hook
- 自定义 Hook 更像是一种约定而不是功能。如果函数的名字以 “
use
” 开头并调用其他 Hook,我们就说这是一个自定义 Hook。useSomething
的命名约定可以让我们的 linter 插件在使用 Hook 的代码中找到 bug。
提示
- 在多个Hook之间传递信息:
useContext
订阅React的Context
useReducer
通过reducer管理组件本地的复杂state,useState
的替代方案。它接收一个形如 (state, action) => newState
的 reducer,并返回当前的 state 以及与其配套的 dispatch
方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
1 |
|
tips:
- 用户定义的组件名必须以大写字母开头(大驼峰):深入 JSX
- 事件命名采用小驼峰式