useState
const [state, setState] = useState(initialState);
useState
返回的第一个值将始终是更新后最新的 state,并且与 class 组件中的 setState
方法不同,useState
不会自己合并更新对象。但是可以通过用函数式的 setState
结合展开运算符来达到合并更新对象的效果。效果如下
setState(prevState => {
// Object.assign 也可以实现这种效果
return {...prevState, ...updatedValues};
});
useEffect
- 1
useEffect 用来完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行(延迟执行) - 2
useEffect
传递的第二个参数,用来控制useEffect
的执行时机,是一个数组。传入空数组 表示只更新一次相当与class
的componentDidMount
只会执行一次。 - 3
受同一种数值影响影响的放在同一块 例如
function Example({ someProp }) {
useEffect(() => {
function doSomething() {
console.log(someProp);
}
doSomething();
}, [someProp]);
}
或者订阅和消除 放在一起
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
});
这样可以控制重复订阅以及 解决没有必要调用 。
- 3
与componentDidMount
、componentDidUpdate
不同,useEffect
的函数会在完成布局与绘制后,被延迟调用。(所以这使得他适用于很多常见你的副作用场景)。然而,并非所有 effect 都需要被延迟执行。例如,一个对用户可见的 DOM 变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别。)React 为此提供了一个额外的useLayoutEffect
Hook 来处理这类 effect。它和useEffect
的结构相同,区别只是调用时机不同。
虽然useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React 总会先清除上一轮渲染的 effect。例子可以参考这里 。
useContext
这个可以获取共享的value的值
const value = useContext(MyContext);
举一个例子 如下
// 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 的数值
return (
<TestContext.Consumer>
{(theme) => {
return (
<View style = {{height:100,backgroundColor:'green'}}>
<Text>{theme}</Text>
</View>
);
}}
</TestContext.Consumer>
);
}
export default MiddleView;
在上面的例子当我改变的时候 只有 MiddleView
和 LoginScreen
发生了改变,如果没有 使用 React.createContext
那么修改 LoginScreen
只会改变
LoginScreen
,BigView
不会重新渲染。这个以后会出一个文章比对说明一下。
useEffect
- 1
createRef 和 useRef 的作用可以说完全一样,但是 useRef hook为DOM节点创建持久引用 ,相当于this 不会发上改变。也就是说 createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用。举一个例子
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 并未发生改变 ,createRef 每次重新render会重新生成新的引用 会随着数字的变化而变化 。
useRef 相当于this createRef 会随着变化而变化。
useReducer
- 1 有一些类似 Redux 。
- 2 useReducer可以使代码具有更好的可读性、可维护性、可预测性。
- 3 子组件中直接通过context拿到dispatch,触发reducer操作state 子组件。对组件层级很深的场景特别有用,不需要一层一层的把 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
计算结果是 return 回来的值, 主要用于 缓存计算结果的值 ,应用场景如: 需要 计算的状态 useMemo 可以用来 细粒度性能优化 。意思也就是说 如果你只希望 组件的 的部分不要进行重新渲染,而不是整个 组件 不要 重新渲染,实现 局部 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
计算结果是 函数, 主要用于缓存函数
,应用场景如: 需要缓存的函数,因为函数式组件每次任何一个 state 的变化 整个组件 都会被重新刷新,一些函数是没有必要被重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费。
-
-
React.memo
与 PureComponent 很相似,但是是专门给 Function Component 提供的,可以支持指定一个参数,可以相当于 shouldComponentUpdate 的作用。有点类似 HOC(高阶组件),在并且内部实现 PureComponent + shouldComponentUpdate 的结合使用。按照下面的例子来说isEqual是用来判断两次 props 是否有,第二个参数不传递,默认只会进行 props 的浅比较。
与 class 组件中shouldComponentUpdate()
方法不同的是,如果 props 相等,areEqual
会返回true
;如果 props 不相等,则返回false
。这与shouldComponentUpdate
方法的返回值相反。
-
function SomeComponent(props) {
/* 组件渲染*/
}
function isEqual(prevProps, nextProps) {
/*
这个方法用来比较前后两次的 Props 是否发生变化 需要自己配置
*/
}
export default React.memo(SomeComponent, isEqual);
- 区别是
useMemo
将调用 fn 函数并返回其结果,而useCallback
将返回 fn 函数而不调用它。React.memo
主要针对组件进行PureComponent
优化。
- 区别是
- 下面我举个例子
在Class Component
中考虑以下的场景:
- 下面我举个例子
class BaseComponent extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <Button onPress={() => this.handleClick()}>Click Me</Button>;
}
}
传给 Button 的 onPress 方法每次都是重新创建
的,这会导致每次 Foo render 的时候,Button 也跟着 render。
优化方法有 2 种,箭头函数
和 bind
。
同样的,Function Component也有这个问题:
function BaseComponent() {
const [count, setCount] = useState(0);
const handleClick() {
console.log(` click happened ${count}`)
}
return <Button onClick={handleClick}>Click Me</Button>;
}
而React
给出的方案是useCallback
。在依赖不变的情况下 (在我们的例子中是 count ),它会返回相同的引用
,避免子组件进行无意义的重复渲染:
function BaseComponent() {
const [count, setCount] = useState(0);
const memoizedHandleClick = useCallback(
() => console.log(`Click happened ${count}`), [count],
);
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}
useMemo缓存的则是方法的返回值。使用场景是减少不必要的子组件渲染:
function BaseComponent({ a, b }) {
// 当 a 改变时才会重新渲染
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// 当 b 改变时才会重新渲染
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
上面的例子只有a
发生改变的时候 Child1
才会改变, b
发生改变的时候 Child2
才会改变。
如果想实现Class Component的shouldComponentUpdate方法,可以使用React.memo方法,区别是它只能比较 props,不会比较 state:
const BaseComponent = React.memo(({ a, b }) => {
// 当 a 改变时才会重新渲染
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// 当 b 改变时才会重新渲染
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
});
useImperativeHandle 和 forwardRef
-
useImperativeHandle
可以让你在使用ref
时自定义暴露给父组件的实例值,useImperativeHandle
应当与forwardRef
一起使用
-
const SomeView = React.forwardRef(({isFollow},ref) => {
const [follow,setFollow] = useState(!!isFollow);
const followRef = useRef();
useEffect(() => {
// do something
},[]);
//这个方法用来导出方法 外面的方法可以通过 Ref.current.function 调用
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 组件调用模块方法的途经.
ok,先总结这么多。
网友评论