useState
const [state, setState] = useState(initialState);
useState
返回的第一個(gè)值將始終是更新后最新的 state偷线,并且與 class 組件中的 setState
方法不同斯入,useState
不會(huì)自己合并更新對(duì)象脖含。但是可以通過用函數(shù)式的 setState
結(jié)合展開運(yùn)算符來達(dá)到合并更新對(duì)象的效果劳坑。效果如下
setState(prevState => {
// Object.assign 也可以實(shí)現(xiàn)這種效果
return {...prevState, ...updatedValues};
});
useEffect
- 1
useEffect 用來完成副作用操作铺厨。賦值給 useEffect 的函數(shù)會(huì)在組件渲染到屏幕之后執(zhí)行(延遲執(zhí)行) - 2
useEffect
傳遞的第二個(gè)參數(shù)偏竟,用來控制useEffect
的執(zhí)行時(shí)機(jī)窍株,是一個(gè)數(shù)組民轴。傳入空數(shù)組 表示只更新一次相當(dāng)與class
的componentDidMount
只會(huì)執(zhí)行一次。 - 3
受同一種數(shù)值影響影響的放在同一塊 例如
function Example({ someProp }) {
useEffect(() => {
function doSomething() {
console.log(someProp);
}
doSomething();
}, [someProp]);
}
或者訂閱和消除 放在一起
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除訂閱
subscription.unsubscribe();
};
});
這樣可以控制重復(fù)訂閱以及 解決沒有必要調(diào)用 球订。
- 3
與componentDidMount
后裸、componentDidUpdate
不同,useEffect
的函數(shù)會(huì)在完成布局與繪制后冒滩,被延遲調(diào)用微驶。(所以這使得他適用于很多常見你的副作用場(chǎng)景)。然而,并非所有 effect 都需要被延遲執(zhí)行因苹。例如苟耻,一個(gè)對(duì)用戶可見的 DOM 變更就必須在瀏覽器執(zhí)行下一次繪制前被同步執(zhí)行,這樣用戶才不會(huì)感覺到視覺上的不一致扶檐。(概念上類似于被動(dòng)監(jiān)聽事件和主動(dòng)監(jiān)聽事件的區(qū)別凶杖。)React 為此提供了一個(gè)額外的useLayoutEffect
Hook 來處理這類 effect。它和useEffect
的結(jié)構(gòu)相同款筑,區(qū)別只是調(diào)用時(shí)機(jī)不同官卡。
雖然useEffect
會(huì)在瀏覽器繪制后延遲執(zhí)行,但會(huì)保證在任何新的渲染前執(zhí)行醋虏。在開始新的更新前寻咒,React 總會(huì)先清除上一輪渲染的 effect。例子可以參考這里 颈嚼。
useContext
這個(gè)可以獲取共享的value的值
const value = useContext(MyContext);
舉一個(gè)例子 如下
// test-context.js
import React from 'react';
export const TestContext = React.createContext('我是傳過去的值');
//LoginScreen.js
import React, {useState,useEffect,useLayoutEffect} from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import BigView from '../Component/BigView';
import {TestContext,themes} from '../Utils/test-context.js';
const LoginScreen = (props) => {
const [value, setValue] = useState("sdssdsdssdd");
const onClick = () => {
setValue((prevState) => {
if (prevState == '我是傳過去的值') {
return '我改變了';
}
return '我是傳過去的值';
});
}
return (
<View style = {{flex:1}}>
<TestContext.Provider value={value}>
<BigView/>
</TestContext.Provider>
</View>
);
}
export default LoginScreen;
//BigView.js
import React, {useEffect} from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import MiddleView from '../Component/MiddleView';
const BigView = (props) => {
useEffect(()=>{
console.log('BigView useEffect begin ');
});
return (
<View style = {{height:200,backgroundColor:'red'}}>
<MiddleView/>
</View>
);
}
export default React.memo(BigView);
import React, { useEffect,useContext} from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import {TestContext,themes} from '../Utils/test-context.js';
const MiddleView = (props) => {
useEffect(()=>{
console.log('MiddleView useEffect begin ');
});
let value = useContext(TestContext);
console.log('value ==============',value);// 這里可以獲取改變的value 的數(shù)值
return (
<TestContext.Consumer>
{(theme) => {
return (
<View style = {{height:100,backgroundColor:'green'}}>
<Text>{theme}</Text>
</View>
);
}}
</TestContext.Consumer>
);
}
export default MiddleView;
在上面的例子當(dāng)我改變的時(shí)候 只有 MiddleView
和 LoginScreen
發(fā)生了改變毛秘,如果沒有 使用 React.createContext
那么修改 LoginScreen
只會(huì)改變
LoginScreen
,BigView
不會(huì)重新渲染阻课。這個(gè)以后會(huì)出一個(gè)文章比對(duì)說明一下叫挟。
useEffect
- 1
createRef 和 useRef 的作用可以說完全一樣,但是 useRef hook為DOM節(jié)點(diǎn)創(chuàng)建持久引用 限煞,相當(dāng)于this 不會(huì)發(fā)上改變抹恳。也就是說 createRef 每次渲染都會(huì)返回一個(gè)新的引用,而 useRef 每次都會(huì)返回相同的引用署驻。舉一個(gè)例子
import React, {useEffect,useState,useRef,createRef} from 'react';
import { View, Text,Button } from 'react-native';
const BigView = (props) => {
const [ index,setIndex ] = useState(0)
const iAmUseRef = useRef();
const iAmCreateRef = createRef();
if (!iAmUseRef.current) {
iAmUseRef.current = index;
}
if (!iAmCreateRef.current) {
iAmCreateRef.current = index;
}
return (
<View style = {{height:200,backgroundColor:'red'}}>
<Button title = {'add index'} onPress = {() => setIndex(index + 1)}/>
<Text>{'current index is ', index}</Text>
<Text>{'useRef.current is ', iAmUseRef.current}</Text>
<Text>{'createRef.current is', iAmCreateRef.current}</Text>
</View>
);
}
export default React.memo(BigView);
很明顯 可以看出 useRef 并未發(fā)生改變 奋献,createRef 每次重新render會(huì)重新生成新的引用 會(huì)隨著數(shù)字的變化而變化 。
useRef 相當(dāng)于this createRef 會(huì)隨著變化而變化旺上。
useReducer
- 1 有一些類似 Redux 瓶蚂。
- 2 useReducer可以使代碼具有更好的可讀性、可維護(hù)性宣吱、可預(yù)測(cè)性窃这。
- 3 子組件中直接通過context拿到dispatch,觸發(fā)reducer操作state 子組件征候。對(duì)組件層級(jí)很深的場(chǎng)景特別有用杭攻,不需要一層一層的把 state 和 callback 往下傳
- 4 惰性初始化
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useCallback 和 useMemo 和 memo
-
useMemo
計(jì)算結(jié)果是 return 回來的值, 主要用于 緩存計(jì)算結(jié)果的值 ,應(yīng)用場(chǎng)景如: 需要 計(jì)算的狀態(tài) useMemo 可以用來 細(xì)粒度性能優(yōu)化 疤坝。意思也就是說 如果你只希望 組件的 的部分不要進(jìn)行重新渲染兆解,而不是整個(gè) 組件 不要 重新渲染,實(shí)現(xiàn) 局部 Pure 功能卒煞。那么可以按照下面這么用痪宰。
-
import React, { useMemo } from 'react';
export default (props = {}) => {
console.log(`--- component re-render ---`);
return useMemo(() => {
console.log(`--- useMemo re-render ---`);
return <div>
<p>number is : {props.number}</p>
</div>
}, [props.number]);
}
-
useCallback
計(jì)算結(jié)果是 函數(shù), 主要用于緩存函數(shù)
,應(yīng)用場(chǎng)景如: 需要緩存的函數(shù),因?yàn)楹瘮?shù)式組件每次任何一個(gè) state 的變化 整個(gè)組件 都會(huì)被重新刷新衣撬,一些函數(shù)是沒有必要被重新刷新的乖订,此時(shí)就應(yīng)該緩存起來,提高性能具练,和減少資源浪費(fèi)乍构。
-
-
React.memo
與 PureComponent 很相似,但是是專門給 Function Component 提供的扛点,可以支持指定一個(gè)參數(shù)哥遮,可以相當(dāng)于 shouldComponentUpdate 的作用。有點(diǎn)類似 HOC(高階組件)陵究,在并且內(nèi)部實(shí)現(xiàn) PureComponent + shouldComponentUpdate 的結(jié)合使用眠饮。按照下面的例子來說isEqual是用來判斷兩次 props 是否有,第二個(gè)參數(shù)不傳遞铜邮,默認(rèn)只會(huì)進(jìn)行 props 的淺比較仪召。
與 class 組件中shouldComponentUpdate()
方法不同的是,如果 props 相等松蒜,areEqual
會(huì)返回true
扔茅;如果 props 不相等,則返回false
秸苗。這與shouldComponentUpdate
方法的返回值相反召娜。
-
function SomeComponent(props) {
/* 組件渲染*/
}
function isEqual(prevProps, nextProps) {
/*
這個(gè)方法用來比較前后兩次的 Props 是否發(fā)生變化 需要自己配置
*/
}
export default React.memo(SomeComponent, isEqual);
- 區(qū)別是
useMemo
將調(diào)用 fn 函數(shù)并返回其結(jié)果,而useCallback
將返回 fn 函數(shù)而不調(diào)用它惊楼。React.memo
主要針對(duì)組件進(jìn)行PureComponent
優(yōu)化玖瘸。
- 區(qū)別是
- 下面我舉個(gè)例子
在Class Component
中考慮以下的場(chǎng)景:
- 下面我舉個(gè)例子
class BaseComponent extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <Button onPress={() => this.handleClick()}>Click Me</Button>;
}
}
傳給 Button 的 onPress 方法每次都是重新創(chuàng)建
的,這會(huì)導(dǎo)致每次 Foo render 的時(shí)候胁后,Button 也跟著 render店读。
優(yōu)化方法有 2 種,箭頭函數(shù)
和 bind
攀芯。
同樣的,F(xiàn)unction Component也有這個(gè)問題:
function BaseComponent() {
const [count, setCount] = useState(0);
const handleClick() {
console.log(` click happened ${count}`)
}
return <Button onClick={handleClick}>Click Me</Button>;
}
而React
給出的方案是useCallback
文虏。在依賴不變的情況下 (在我們的例子中是 count )侣诺,它會(huì)返回相同的引用
,避免子組件進(jìn)行無意義的重復(fù)渲染:
function BaseComponent() {
const [count, setCount] = useState(0);
const memoizedHandleClick = useCallback(
() => console.log(`Click happened ${count}`), [count],
);
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}
useMemo緩存的則是方法的返回值氧秘。使用場(chǎng)景是減少不必要的子組件渲染:
function BaseComponent({ a, b }) {
// 當(dāng) a 改變時(shí)才會(huì)重新渲染
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// 當(dāng) b 改變時(shí)才會(huì)重新渲染
const child2 = useMemo(() => <Child2 b=年鸳 />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
上面的例子只有a
發(fā)生改變的時(shí)候 Child1
才會(huì)改變, b
發(fā)生改變的時(shí)候 Child2
才會(huì)改變。
如果想實(shí)現(xiàn)Class Component的shouldComponentUpdate方法丸相,可以使用React.memo方法搔确,區(qū)別是它只能比較 props,不會(huì)比較 state:
const BaseComponent = React.memo(({ a, b }) => {
// 當(dāng) a 改變時(shí)才會(huì)重新渲染
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// 當(dāng) b 改變時(shí)才會(huì)重新渲染
const child2 = useMemo(() => <Child2 b= />, [b]);
return (
<>
{child1}
{child2}
</>
)
});
useImperativeHandle 和 forwardRef
-
useImperativeHandle
可以讓你在使用ref
時(shí)自定義暴露給父組件的實(shí)例值,useImperativeHandle
應(yīng)當(dāng)與forwardRef
一起使用
-
const SomeView = React.forwardRef(({isFollow},ref) => {
const [follow,setFollow] = useState(!!isFollow);
const followRef = useRef();
useEffect(() => {
// do something
},[]);
//這個(gè)方法用來導(dǎo)出方法 外面的方法可以通過 Ref.current.function 調(diào)用
useImperativeHandle(ref, () => ({
setFollowState: (isFollow) => {
if (!!followRef) {
followRef.current.setFollowState(isFollow);
}
}
}));
return(
<View style = {{
width: 750,
height: 450
backgroundColor:'rgba(1,5,13,0.7)'}}>
<FollowButton
ref = {followRef}
isFollow = {follow} />
</View>
);
});
const BigView = (props) => {
const someViewRef = useRef();
const onChange = () => {
someViewRef.current.setFollowState(false);
}
return (
<View style = {{flex:1}}>
<SomeView />
<Button title = {'切換'} ref = {someViewRef} onPress = {onChange} />
</View>
);
}
export default React.memo(BigView);
從上面可以看出 useImperativeHandle
是function 組件調(diào)用模塊方法的途經(jīng).
ok膳算,先總結(jié)這么多座硕。