react hook小记

2021-05-24  本文已影响0人  三粒黑子球

useState

const [state, setState] = useState(initialState);

useState 返回的第一个值将始终是更新后最新的 state,并且与 class 组件中的 setState 方法不同,useState 不会自己合并更新对象。但是可以通过用函数式的 setState 结合展开运算符来达到合并更新对象的效果。效果如下

setState(prevState => {
  //  Object.assign 也可以实现这种效果 
  return {...prevState, ...updatedValues};
});

useEffect

function Example({ someProp }) {
  useEffect(() => {
    function doSomething() {
      console.log(someProp);
    }

    doSomething();
  }, [someProp]); 
}

或者订阅和消除 放在一起

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // 清除订阅
    subscription.unsubscribe();
  };
});

这样可以控制重复订阅以及 解决没有必要调用 。

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;

在上面的例子当我改变的时候 只有 MiddleViewLoginScreen发生了改变,如果没有 使用 React.createContext那么修改 LoginScreen 只会改变
LoginScreenBigView 不会重新渲染。这个以后会出一个文章比对说明一下。

useEffect

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

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

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]);
}
function SomeComponent(props) {
  /* 组件渲染*/
}
function isEqual(prevProps, nextProps) {
  /*
  这个方法用来比较前后两次的 Props 是否发生变化 需要自己配置 
  */
}
export default React.memo(SomeComponent, isEqual);
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

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,先总结这么多。

上一篇下一篇

猜你喜欢

热点阅读