React Hooks
为什么使用 React Hook
6 Reasons to Use React Hooks Instead of Classes
useState hook
- useState hook用来render state variable
- 标准写法:
const [stateVariable, setStateVariable function] = useState();
import React, { useState } from "react";
const StateTutorial = () => {
//inputValue是state variable
//setInputValue是function
//只能通过setInputValue对state variable进行修改
const [inputValue, setInputValue] = useState("Pedro");
let onChange = (event) => {
//因为setInputValue是异步的, 所以需要先将event.target赋值出来
const newValue = event.target.value;
setInputValue(newValue);
};
return (
<div>
<input placeholder="enter something..." onChange={onChange} />
{inputValue}
</div>
);
};
export default StateTutorial;
实现效果: 右边同时更新左边输入的字符串
实际开发例子:
- state variable selectedChildren 是一个数组
- 通过onChange调用setSeelctedChildren, 对selectedChildren进行修改
- selectedChildren通过value props传给组件, 渲染到屏幕上
- setStateVariable function不是Web API和server call 为什么还是异步的?
- This is because setState alters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive. Thus the setState calls are asynchronous as well as batched for better UI experience and performance.
useReducer hook
- 当同时需要update多个state时可以使用useReducer
const ReducerTutorial = () => {
const [count, setCount] = useState(0);
const [showText, setShowText] = useState(true);
return (
<div>
<h1>{state.count}</h1>
<button
onClick={() => {
setCount(count + 1);
setShowText(!showText);
}}
>
Click Here
</button>
{state.showText && <p>This is a text</p>}
</div>
);
}
上面的代码可以使用useReducer完成, 如下
import React, { useReducer } from "react";
const reducer = (state, action) => {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1, showText: state.showText };
case "toggleShowText":
return { count: state.count, showText: !state.showText };
default:
return state;
}
};
const ReducerTutorial = () => {
const [state, dispatch] = useReducer(reducer, { count: 0, showText: true });
return (
<div>
<h1>{state.count}</h1>
<button
onClick={() => {
dispatch({ type: "INCREMENT" });
dispatch({ type: "toggleShowText" });
}}
>
Click Here
</button>
{state.showText && <p>This is a text</p>}
</div>
);
};
export default ReducerTutorial;
useEffect hook
- 当每次页面render时, useEffect都会被执行
- 除去刷新页面外, state variable被rerender时也会执行 useEffect
标准写法:
useEffect(() => {
do something );
}, []);
- 后面的[]表示哪些state variable被更新时会执行useEffect
[ ]
为空表示任何state varibale更新时都不会执行useEffect. 只有按刷新页面时才会执行useEffect- 如果没有
[ ]
则表示任何state varibale更新时都会执行useEffect
import React, { useEffect, useState } from "react";
import axios from "axios";
function EffectTutorial() {
const [data, setData] = useState("");
const [count, setCount] = useState(0);
//刷新页面时, 会调用API
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/comments")
.then((response) => {
setData(response.data[0].email);
console.log("API WAS CALLED");
});
}, []);
return (
<div>
Hello World
<h1>{data}</h1>
<h1>{count}</h1>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click![在这里插入图片描述](https://im![在这里插入图片描述](https://img-blog.csdnimg.cn/cc6c700276d04ee084fac886150d282c.png)
g-blog.csdnimg.cn/30669808ec2e4f68b78041512bf23964.png)
</button>
</div>
);
}
export default EffectTutorial;
useRef hook
- persist values between renderers beside use state. — 也就是在不渲染画面的情况下, 可以在component中储存变量
useLayoutEffect hook
- useEffect会在渲染的内容到DOM上后执行,不会阻塞DOM的渲染
- 也就是先渲染页面 (会显示未更新的state variable), 然后再更新state variable. (因为渲染和更新的非常快, 所以察觉不出来)
- useLayoutEffect会在渲染的内容到DOM上之前进行,会阻塞DOM的渲染
import { useLayoutEffect, useEffect, useRef } from "react";
function LayoutEffectTutorial() {
const inputRef = useRef(null);
useLayoutEffect(() => {
//先执行这里
console.log(inputRef.current.value);
}, []);
useEffect(() => {
//再执行这里
inputRef.current.value = "HELLO";
}, []);
return (
<div className="App">
<input ref={inputRef} value="PEDRO" style={{ width: 400, height: 60 }} />
</div>
);
}
export default LayoutEffectTutorial;
useImperativeHandle hook
- 可以在上级组件中控制子组件的state
import React, { useRef } from "react";
import Button from "./Button";
function ImperativeHandle() {
const buttonRef = useRef(null);
return (
<div>
<button
onClick={() => {
buttonRef.current.alterToggle();
}}
>
Button From Parent
</button>
<Button ref={buttonRef} />
</div>
);
}
export default ImperativeHandle;
import React, { forwardRef, useImperativeHandle, useState } from "react";
const Button = forwardRef((props, ref) => {
const [toggle, setToggle] = useState(false);
useImperativeHandle(ref, () => ({
alterToggle() {
setToggle(!toggle);
},
}));
return (
<>
<button>Button From Child</button>
{toggle && <span>Toggle</span>}
</>
);
});
export default Button;
useMemo
- 用于性能优化
import axios from "axios";
import { useEffect, useState, useMemo } from "react";
export default function MemoTutorial() {
const [data, setData] = useState(null);
const [toggle, setToggle] = useState(false);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/comments")
.then((response) => {
setData(response.data);
});
}, []);
const findLongestName = (comments) => {
if (!comments) return null;
let longestName = "";
for (let i = 0; i < comments.length; i++) {
let currentName = comments[i].name;
if (currentName.length > longestName.length) {
longestName = currentName;
}
}
console.log("THIS WAS COMPUTED");
return longestName;
};
return (
<div className="App">
//这里每当页面渲染, 都会执行这个函数, 如果数据量很大, 会降低性能
<div> {findLongestName(data)} </div>
<button
onClick={() => {
setToggle(!toggle);
}}
>
{" "}
Toggle
</button>
{toggle && <h1> toggle </h1>}
</div>
);
}
使用useMemo改进
import axios from "axios";
import { useEffect, useState, useMemo } from "react";
export default function MemoTutorial() {
const [data, setData] = useState(null);
const [toggle, setToggle] = useState(false);
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/comments")
.then((response) => {
setData(response.data);
});
}, []);
const findLongestName = (comments) => {
if (!comments) return null;
let longestName = "";
for (let i = 0; i < comments.length; i++) {
let currentName = comments[i].name;
if (currentName.length > longestName.length) {
longestName = currentName;
}
}
console.log("THIS WAS COMPUTED");
return longestName;
};
const getLongestName = useMemo(() => findLongestName(data), [toggle]); //只有当data改变时才执行这个函数
return (
<div className="App">
<div> {getLongestName} </div>
<button
onClick={() => {
setToggle(!toggle);
}}
>
{" "}
Toggle
</button>
{toggle && <h1> toggle </h1>}
</div>
);
}