产生背景
- Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
- react hooks 要解决的问题是状态共享,(自定义hook)是继render-props和HOC组件之后的第三种状态共享方案,不会产生jsx嵌套地狱的问题。
- Hook 使你在无需修改组件结构的情况下复用状态逻辑。
状态共享的方式
- render-props方式
render prop就是给组件添加一个值为函数的属性,这个函数可以在组件渲染(fn)的时候调用
class Mouse extends React.Component{
constructor(props){
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove=(event)=>{
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div onMouseMove={this.handleMouseMove}>
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
{this.props.fn(this.state)} // 这个比较关键 fn 就是这个函数的属性
</div>
);
}
}
//另一个组件cat,需要拿到mouse的数据
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img
src="/cat.jpg"
style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
//组合这两个组件
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse fn={mouse => (//将Mouse组件中的this.state拿到传递给cat组件
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
//相当于cat组件使用了mouse组件,以后再有的dog,pig组件都可以使用mouse组件(实现mouse的重用);
- HOC组件(高阶组件是参数为组件,返回值为新组件的函数)
//每个经过高阶组件处理过的组件都会复用高阶组件里边的所有逻辑,
//但原则是高阶组件是一个纯函数,不会修改传入的组件
function high(Component){
return class extends React.Component{
constructor(){
this.state={
open:false
}
}
componentDidMount(){
console.log('haha')
}
change=()=>{
this.setState((state)=>{//用到state需要使用回调函数修改state的值
return {open:!state.open}
})
}
render(){
return <Component open={this.state.open} change={this.change}>
</Component>
}
}
}
class ToggleButton extends Component{//不带有自身的状态能够实现组件的复用
constructor(props){
super(props)
}
render(){
let {open,change}=this.props;
return <Fragment>
<button type="primary" onClick={change}>
toggle Modal
</button>
<div>{open}</div> //拿到open值
</Fragment>
}
}
const EnhancedComponent = high(ToggleButton);
内置hooks
-
useState
像是 function 形式的 setState,其实这等价于依赖注入,与使用 setState 相比,这个组件是没有状态的。
当不使用hooks时,需要使用class创建组件,使用state作为内部私有状态
class ToggleBotton extends React.Component{
constructor(props){
super(props);
this.state={open:false}
}
toggle=()=>{
this.setState((state)=>{
return {open:!state.open}
})
}
render(){
<Fragment>
<button type="primary" onClick={this.toggle}>
toggle Modal
</button>
<div>{this.state.open}</div> //拿到open值
</Fragment>
}
}
使用hooks的useState
function ToggleBotton() {
const [open, setOpen] = useState(false);
//useState接收一个默认值(可以是任何数据类型),
//返回数组有一对值,不带set的用来取值,带set的用来修改值,可以取任何名字
const toggle=()=>{
setOpen(!open)//直接使用setOpen修改open值,不用使用setState
}
return (
<Fragment>
<button type="primary" onClick={toggle}>
toggle Modal
</button>
<div>{open}</div> //直接用 open取值,不用使用this.state.open
</Fragment>
);
}
useState在一个组件内可以多次调用,也可以将参数设置为对象数据类型,减少调用
const [instete, setInstete] = useState({aa:1,bb:2,cc:3...});
-
useEffect
- 可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
- 接收一个回调函数做参数
- 本要在componentWillUnmount中要编写的逻辑,放在 useEffect执行的回调函数中 返回的一个新函数中。。。
- 一个组件中可以调用多个useEffect,不同的逻辑放在各自的useEffect中,使逻辑更加清晰,当组件初始或重新渲染时会依次执行所有的useEffect
- 可以通过跳过 Effect 进行性能优化
useEffect(() => {
console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
const subscription = props.source.subscribe();
return () => {//返回的函数用来清除计时器,解绑事件啥的,防止内存泄漏
//相当于componentWillUnmount要编写的逻辑,如果没有这些需求也可以不写
subscription.unsubscribe();
};
});
以上代码等价于
componentDidMount(){
console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
const subscription = props.source.subscribe();
}
componentDidUpdate(){
console.log('useEffect 的代码既会在初始化时候执行,也会在后续每次重新渲染时执行')
const subscription = props.source.subscribe();
}
componentWillUnmount{
subscription.unsubscribe();
}
effect的性能优化(useEffect的第二个参数)
//第二个参数是一个数组,说明当数组里边的数据发生变化的时候才会触发调用useEffect的回调
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时才更新,避免多余的调用
如果只想在初始化的时候实现依次调用的化,第二个参数写成空数组 [ ] 即可
自定义hooks(解决状态共享)
1. hooks的规则
- 不要在循环,条件或嵌套函数中调用 Hook
- 不要在普通的 JavaScript 函数中调用 Hook
✅ 在 React 的函数组件中调用 Hook
✅ 在自定义 Hook 中调用其他 Hook - Hook 函数必须以 "use" 命名开头
2.自定义hooks的使用
- 自定义 Hook 是一个函数,其名称必须以 “use” 开头,函数内部可以调用其他的 Hook。
- 函数式组件都可以使用这个自定义的hook
- 在两个组件中使用相同的 Hook 不会共享 state,不同组件之间的state没有关系,所以也不能实现数据的持久化
- 你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器。。。
import React, { useState, useEffect } from 'react';
//定义自定义hook
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
//使用自定义hook
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
hooks的特点
- 多个状态不会产生嵌套,写法还是平铺的(renderProps 可以通过 compose 解决,可不但使用略为繁琐,而且因为强制封装一个新对象而增加了实体数量)。
- Hooks 可以引用其他 Hooks。
- 更容易将组件的 UI 与状态分离。
最后的目标是----有状态的组件没有渲染,有渲染的组件没有状态
状态和ui分离,便于逻辑的复用
网友评论