美文网首页
react hook小记

react hook小记

作者: 三粒黑子球 | 来源:发表于2021-05-24 11:23 被阅读0次

    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的执行时机,是一个数组。传入空数组 表示只更新一次相当与classcomponentDidMount 只会执行一次。
    • 3
      受同一种数值影响影响的放在同一块 例如
    function Example({ someProp }) {
      useEffect(() => {
        function doSomething() {
          console.log(someProp);
        }
    
        doSomething();
      }, [someProp]); 
    }
    

    或者订阅和消除 放在一起

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

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

    • 3
      componentDidMountcomponentDidUpdate 不同,useEffect 的函数会在完成布局与绘制后,被延迟调用。(所以这使得他适用于很多常见你的副作用场景)。然而,并非所有 effect 都需要被延迟执行。例如,一个对用户可见的 DOM 变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别。)React 为此提供了一个额外的 useLayoutEffectHook 来处理这类 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;
    

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

    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

      1. 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]);
    }
    
      1. useCallback 计算结果是 函数, 主要用于 缓存函数,应用场景如: 需要缓存的函数,因为函数式组件每次任何一个 state 的变化 整个组件 都会被重新刷新,一些函数是没有必要被重新刷新的,此时就应该缓存起来,提高性能,和减少资源浪费。
      1. 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);
    
      1. 区别是 useMemo 将调用 fn 函数并返回其结果,而useCallback 将返回 fn 函数而不调用它。React.memo主要针对组件进行 PureComponent优化。
      1. 下面我举个例子
        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

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

    相关文章

      网友评论

          本文标题:react hook小记

          本文链接:https://www.haomeiwen.com/subject/rtledltx.html