react hook小记

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • 前言 本文全面介绍了React Hooks的所有API概念、用法、丰富的demo以及部分底层原理。 实际上,Rea...
    南宫__阅读 3,575评论 0 6
  • 使用 React Hooks 相比于从前的类组件有以下几点好处: 1.代码可读性更强,原本同一块功能的代码逻辑被拆...
    Kinderzhu阅读 862评论 0 2
  • 在学会使用React Hooks之前,可以先看一下相关原理学习React Hooks 前言 在 React 的世界...
    DC_er阅读 9,082评论 1 16
  • 在React v16.8新增了Hook,它提供了在函数组件中访问状态和React生命周期等能力,这些函数可以在程序...
    小小小小小粽子阅读 490评论 0 0
  • react 16.8 以后加上了 react hook,它可以让你在不编写 class 的情况下使用 state ...
    forever_提拉米苏阅读 520评论 1 6