Hooks
Hook 是React 16.8的新特性,他可以让你在 function 组件中使用 state 以及其他的 React 特性。
Hooks的特点:
- 使你在无需修改组件结构的情况下复用状态逻辑
- 可将组件中相互关联的部分拆分成更小的函数,复杂组件将变得更容易理解
- 更简洁、更易理解代码
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
- 只能在 React 的函数组件中调用 Hook
useState
useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
import React, { useState } from 'react';
// 声明列表组件
function FruitList ({ fruits, onSetFruit }) {
return (
<ul>
{ fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
</ul>
)
}
// 声明输入组件
function FruitAdd (props) {
const [pname, setPname] = useState("") // 键盘事件处理
const onAddFruit = e => {
if (e.key === "Enter") {
props.onAddFruit(pname);
setPname("");
}
}
return (
<div>
<input
placeholder="请输入需要添加的水果"
type="text"
value={pname}
onChange={e => setPname(e.target.value)}
onKeyDown={onAddFruit}
/>
</div>
)
}
export default function HooksTest() {
// useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
const [fruit, setFruit] = useState("")
const [fruits, setFruits] = useState(['香蕉', '草莓', '芒果'])
return (
<div>
<p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
<FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
<FruitAdd onAddFruit={pname => setFruits([...fruits, pname])}></FruitAdd>
</div>
);
}
useEffect
useEffect 给函数组件增加了执行副作用操作的能力。
副作用(Side Effect)是指一个 function 做了和本身运算、返回值无关的事,比如:修改了全局变量、修改了传入的 参数、甚至是 console.log(),所以 ajax 操作,修改 dom 都是算作副作用。
- 异步获取数据 ```javascript import { useEffect } from “react”;
useEffect(()=>{ setTimeout(() => { setFruits([‘香蕉’,’西瓜’]) }, 1000); },[]) // 设置空数组意为没有依赖,则副作用操作仅执行一次
如果副作用对某状态有依赖,需要添加依赖选项
```javascript
useEffect(() => {
document.title = fruit;
}, [fruit]);
清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏
useEffect(() => {
const timer = setInterval(() => {
console.log('msg')
}, 1000)
// 返回一个清理函数,组件卸载后会执行清理函数
return function () {
clearInterval(timer)
}
})
useReducer
useReducer是useState的可选项,常用于组件有复杂状态逻辑时,类似于redux中reducer概念。
范例:使用 useReducer 改写上面的代码
import React, { useState, useEffect, useReducer } from 'react';
function FruitList ({ fruits, onSetFruit }) {
return (
<ul>
{ fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
</ul>
)
}
// 声明输入组件
function FruitAdd (props) {
const [pname, setPname] = useState("") // 键盘事件处理
const onAddFruit = e => {
if (e.key === "Enter") {
props.onAddFruit(pname);
setPname("");
}
}
return (
<div>
<input
placeholder="请输入想要添加的水果"
type="text"
value={pname}
onChange={e => setPname(e.target.value)}
onKeyDown={onAddFruit}
/>
</div>
)
}
// 添加fruit状态维护fruitReducer
function fruitReducer (state, action) {
switch(action.type) {
case "init":
return action.payload
case "add":
return [...state, action.payload]
default:
return state
}
}
export default function HooksTest() {
// useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
const [fruit, setFruit] = useState("")
// 接收一个 reducer 和初始值,返回 state 和 dispatch
const [fruits, dispatch] = useReducer(fruitReducer, [])
// useEffect 接收回调函数和一个依赖数组(依赖为空时,useEffect函数只执行一次)
useEffect(() => {
setTimeout(() => {
// setFruits(['香蕉', '草莓', '芒果'])
dispatch({ type: 'init', payload: ['香蕉', '草莓', '芒果']})
}, 1000)
}, [])
// 如果副作用操作对某状态有依赖,需要添加依赖选项
useEffect(() => {
document.title = fruit; },
[fruit]);
// 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏
useEffect(() => {
const timer = setInterval(() => {
console.log('msg')
}, 1000)
// 返回一个清理函数,组件卸载后会执行清理函数
return function () {
clearInterval(timer)
}
})
return (
<div>
<p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
<FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
<FruitAdd onAddFruit={pname => dispatch({ type: 'add', payload: pname })}></FruitAdd>
</div>
);
}
useContext
useContext用于在快速在函数组件中导入上下文,useContext可以实现组件之间的解耦,复杂组件会更变得更加容易理解。
import React, { useState, useEffect, useReducer, useContext } from 'react';
function FruitList ({ fruits, onSetFruit }) {
return (
<ul>
{ fruits.map(item => <li onClick={ () => onSetFruit(item)} key={item}>{item}</li>) }
</ul>
)
}
// 声明输入组件
function FruitAdd (props) {
// 使用 useContent 获取上下文
const { dispatch } = useContext(Context)
const [pname, setPname] = useState("") // 键盘事件处理
const onAddFruit = e => {
if (e.key === "Enter") {
// props.onAddFruit(pname);
dispatch({ type: 'add', payload: pname })
setPname("");
}
}
return (
<div>
<input
placeholder="请输入想要添加的水果"
type="text"
value={pname}
onChange={e => setPname(e.target.value)}
onKeyDown={onAddFruit}
/>
</div>
)
}
// 添加fruit状态维护fruitReducer
function fruitReducer (state, action) {
switch(action.type) {
case "init":
return action.payload
case "add":
return [...state, action.payload]
default:
return state
}
}
// 创建上下文对象
const Context = React.createContext()
export default function HooksTest() {
// useState(initialState) 接收初始状态,返回一个由状态和更新函数组成的数组
const [fruit, setFruit] = useState("")
// const [fruits, setFruits] = useState([])
// 接收一个 reducer 和初始值,返回 state 和 dispatch
const [fruits, dispatch] = useReducer(fruitReducer, [])
// useEffect 接收回调函数和一个依赖数组(依赖为空时,useEffect函数只执行一次)
useEffect(() => {
setTimeout(() => {
// setFruits(['香蕉', '草莓', '芒果'])
dispatch({ type: 'init', payload: ['香蕉', '草莓', '芒果']})
}, 1000)
}, [])
// 如果副作用操作对某状态有依赖,需要添加依赖选项
useEffect(() => {
document.title = fruit; },
[fruit]);
// 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止内存泄漏
useEffect(() => {
const timer = setInterval(() => {
console.log('msg')
}, 1000)
// 返回一个清理函数,组件卸载后会执行清理函数
return function () {
clearInterval(timer)
}
})
return (
<Context.Provider value={{ fruits, dispatch }}>
<div>
<p>{ fruit === "" ? "请选择喜爱的水果" : `你的的选择是:${fruit}` }</p>
<FruitList fruits={fruits} onSetFruit={setFruit}></FruitList>
<FruitAdd></FruitAdd>
</div>
</Context.Provider>
);
}