函数式组件
function HelloMessage() {
let message = 'hello message'
return (
<div>
<div>{messgae}</div>
<button onClick={() => { message = 'hello react' }}>修改文本</button>
</div>
)
}
函数式组件的缺陷:
- 组件不会重新渲染:修改message之后,组件不知道自己要重新渲染
- 如果组件可以重新渲染,函数会被重新执行,第二次执行时message又会被初始化为"hello message"
- 没有生命周期的回调
类组件
function HelloWorld (props) {
const imitateRequest = () => {
setTimeout(() => {
console.log('模拟函数式组件发送请求')
}, 1000)
}
imitateRequest()
return (
<div>
<h2>Hello World: {props.counter}</h2>
</div>
)
}
class App extends PureComponent {
constructor (props) {
super(props)
this.state = {
counter: 1
}
}
increment () {
this.setState({
counter: this.state.counter + 1
})
}
render() {
let { counter } = this.state
return (
<div>
<h1>App:{counter}</h1>
<button onClick={this.increment}>修改counter</button>
<HelloWorld counter={counter}/>
</div>
)
}
}
class组件相对于函数式组件有什么优势?比较常见的是下面的优势:
-
class可以定义自己的state,用来保存组件内部的状态
- 而函数式组件不可以,因为函数每次调用,都会产生新的临时变量
-
class有自己的生命周期,我们可以在对应的生命周期中编写自己的逻辑
- 比如在componentDidMount中发送网络请求,这个生命周期只会执行一次
- 函数式组件,如果在函数中发送网络请求,每次重新渲染都会重新发送一次网络请求
-
class组件在状态发生变化时,只会重新执行render函数和componentDidupdate等
- 函数式组件在重新渲染时,整个函数都会被执行
class组件的缺点:
复杂组件变得难以理解:
对于逻辑复杂的组件,代码会比较混乱,比如componentDidMount中,就可能包含大量的逻辑代码,比如网络请求,事件监听等。如果想要抽取通用的逻辑,比如高阶组件(HOC),又增加代码的复杂度。
难以理解的class: 要学习class,还必须搞清楚this的指向
-
组件复用状态很难:
- 通过高阶组件复用状态
- Provider和Consumer来共享状态,但是多次使用Consumer时,代码就会存在很多嵌套
- 这些代码不管是编写和设计,都比较困难
Hooks的出现
hooks的出现解决函数式组件的问题,可以让我们在函数式组件中使用state以及react的其他特性。
const Home = () => {
const [name, setName] = useState('hello title')
const handleName = ({ target: {value}}) => {
setName(value)
}
useEffect(() => {
document.title = name
}, [name])
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
let handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [])
return (
<div>
<input value={name} onChange={handleName}/>
<span>{width}</span>
</div>
)
}
Hooks的好处:
-
函数式组件结合hooks让整个代码变得非常简洁
- 利用useEffect,把业务逻辑分开,把之前放在不同生命周期里的相同功能的代码,聚合在一起,把不同功能但是要在同一生命周期的代码,隔离开,提高了代码的可读性和可维护性。
-
解决了和状态相关的逻辑复用问题:
- 原来代码复用使用的高阶组件和渲染属性都会增加组件树层级,使用起来也不方便,自定义hooks就可以解决这些问题,而且使用更直观方便;
并且再也不用考虑this相关的问题;
-
更好的体现了react的开发思想,从state->view的函数式映射
- 可以把UI的展示看成一个函数执行的过程,Model就相当于输入的参数,UI就相当于函数的执行结果;react要保证的是每当状态发生变化时,函数就会重新执行,并且会生成新的dom树,然后react会把新的dom树,以最优的方式更新到浏览器上;
Hooks的注意事项:
- 只能在函数最外层调用hook,不能在循环、条件判断或者子函数中调用
- 只能在React的函数组件或者自定义Hooks中调用Hook,不能在其他js函数中调用
- 保证状态最小化,需要渲染到UI里的保存在state里,不需要渲染的可以保存在useRef里;或者一些可以通过其他状态计算得出的数据;
- 不要用props去初始化state,可以在useEffect中通过监听props的变化去设置state;
有了Hooks以后得函数式组件,依然存在一些问题:
-
事件处理函数会被重复定义,数据计算过程没有缓存
- 这些问题就需要利用useCallback、useMemo、useRef这些Hook来做一些优化
对一些类组件的生命周期函数还不支持,还不能实现getSnapShotBeforeUpdate和componentDidCatch这个生命周期;