React 组件
React 组件是什么?如何构建一个 React组件。
react
中组件的形式有两种 函数组件以及类组件,函数组件很好理解只要最终返回一个虚拟树就可以了,这中间的逻辑完全就是 js
的逻辑,没有什么好记录的,但类组件涉及到父类以及 state
需要记录一下。
类组件
state
state
可以说是 react
实现数据变化更新视图的最重要的一部分。
state 干嘛用?
state
表示一个组件的内部可变数据的集合,为什么说是可变的数据呢?
不变的数据用 this
保存即可,因为不会发生变化,所以不用触发视图更新。
没了,作用就是保存数据。那么如何触发视图更新呢?
这就需要用到 setState
。
setState 是什么?
setState
是一个函数,该函数接收一个对象或接收一个函数。
setState 做了什么?
this.setState({...})
执行之后发生了什么?总的来说,有以下步骤:
- 获取原先
state
保存的数据:prevState
- 将传入的对象合并到
prevState
生成:nextState
- 将
this.state = nextState
- 通知虚拟树的根组件重新生成虚拟树(调用函数组件或是类组件的
render
方法) - 将新的虚拟树与之前的虚拟树作对比(
diff
算法),找出差别,生成更新程序(patch
) - 执行更新程序
html
页面需要更新的地方就会发生变化
setState 参数为函数的情况
this.setState((prevState, props) => ({...}))
与对象形式的 setState
区别在于第三点,函数形式为
- 将
this.state = 函数调用的结果
setState 的内容是异步执行
以上不管是对象形式还是函数形式,上面所说的 6
步都会异步执行。
this.state = {a: 1};
this.setState({a: this.state.a + 1})
this.setState({a: this.state.a + 1})
由于是异步执行,当执行到第二个 setState
时,this.state
的状态还未改变,因此设置的值仍为 2
。问题出在获取 state
的方式,如果为函数形式,那么 state
就能被正确的获取(因为 state
会被当做参数传入,每次传入的都是最新的 state
)。
this.setState(prevState => ({a: prevState.a + 1}))
this.setState(prevState => ({a: prevState.a + 1}))
state 总结 & 补充
state
中存放变动的数据,不变动的放在this
下即可setState
会触发梗元素重新生成虚拟树- 重新生成整颗虚拟树的结构就是所有的组件都重新被调用了
setState
是类继承过来的方法,不应该被覆盖
生命周期
生命周期函数的意义:组件获取虚拟树过程中会执行的一系列方法。

函数组件
函数组件在 v16
版本之前确实没什么好记的,只需要记住组件的传入参数是组件的 props
即可。
在 v16
版本之后出现了 hooks
记录一下
useState
useState
为 react
函数组件提供了保存状态的能力。使用如下
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
函数需传入一个参数,返回一个数组,数组第一项为传入的值,数组第二项为一个函数,可以修改原始值。
该函数的目的在于让函数组件也拥有 state
,大概可以知道具体的执行过程。
- 通过数组返回的第二个函数修改原始值
useState
内部状态变化- 通知虚拟树的重新生成虚拟树
- 将新的虚拟树与之前的虚拟树作对比(
diff
算法),找出差别,生成更新程序(patch
) - 执行更新程序
html
页面需要更新的地方就会发生变化
其实和类组件的效果差不多,就是代码量变少了,但如果涉及复杂的组件,感觉还是类组件好用,类组件有个 this
。
useEffect
useEffect
让 react
函数组件拥有生命周期的钩子。使用如下
import { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// 明确在这个 effect 之后如何清理它
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
useEffect
中传入的函数将会在组件初始化完成的时候调用,而调用的结果(结果是一个函数),将会在组件销毁的时候调用。
useRef
useRef
让 react
函数组件能使用 ref
。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
用法和 React.createRef()
一样,感觉这个 api
可以直接用于函数组件,哪天试一下。
useContext
useContext
需要搭配 React.createContext
使用,用于直接获取 Context
。
和组件的概念不一样,组件内部一般是不主动去获取 Context
而是通过 Provider
和 Consumer
来获取后,通过 props
传入组件内部,useContext
直接获取到最近的 Provider
处,拿到提供的值,如果没有 Provider
则取默认值,也就是传入 React.createContext
这个方法的值。
useReducer
useReducer
提供一个类似 redux
的 api
,需要一个 reducer
。
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
const Com = props => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div value={{ state, dispatch }}>
<botton onClick={()=>dispatch({type: 'increment'})}>+</botton>
<botton onClick={()=>dispatch({type: 'decrement'})}>-</botton>
{state.count}
</div>
);
};
当然 dispatch
也可通过 props
向子元素传递。
其他 hooks
一些目的性不强,个人感觉为了 hook
而 hook
的就不写了,总之如果逻辑简单,而函数组件满足不了时,使用这些 hook
,但逻辑复杂还是使用类组件比较好。