Hooks 常用方法

82 阅读4分钟

zh-hans.reactjs.org/docs/hooks-…Hook API 索引

useSelector

 react-redux@7 的 useSelector API . 用来获取redux里抛出的redux变量

import {useSelector} from 'react-redux';

//组件里
const state = useSelector(state => {
    return {
      chatBadgeCount: state.chat.chatBadgeCount,
      letterBadgeCount: state.sideMenu.letterBadgeCount,
    };
  });

//使用
         <MsgBubbleCom
                  wd={px2dp(18)}
                  hg={px2dp(18)}
                  account={state[item.markKey]}
                  color={'white'}
                />

useMemo/useCallback

juejin.cn/post/684490…

memo是用来优化函数组件的重渲染问题,当传入的属性值都没变化时就不会触发组件的重渲染,否则组件就会重渲染. (传入子组件的值没有变化时,不让子组件渲染, 包裹组件 )

当父组件引入子组件的情况下,往往会造成组件之间的一些不必要的浪费.

const Count = memo(function Count(props) {
  console.log('count render')
  return <div>子组件count值:{props.double}</div>
})
//只有double改变的时候,才会重新渲染组件
   <Count double={double} />

useMemo功能是判断组件中的函数逻辑是否重新执行,用来优化性能。

(子组件依赖几个值,值变了,子组件也变化, 包裹函数 )

useMemo的用法与useEffect非常相似,如果第二个参数为空则函数组件每次被渲染,useMemo内的逻辑都会被执行。如果第二个参数为一个空数组,那么仅会在组件第一次被渲染时执行。其他的情况都是在数组内元素完全相同时才不执行

  const [count, setCount] = useState(0);

  //第一个参数是要执行的函数
  //第二个参数是执行函数依赖的变量组成的数据
  //这里只有count发生变化double才会重新计算
  const double = useMemo(() => {
    return count * 2;
  }, [count])
     <Count double={double} />
       
const container = React.useMemo(() => {
    return (
      <Animated.View
        style={[
          styles.container,
          styles.center,
          {
            height: barHeight,
          },
        ]}>
        {toolsIcon}
      </Animated.View>
    );
  }, [barHeight, toolsIcon]);

useCallback

useMome、useCallback用法都差不多,都会在第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行,并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。

const value = useMemo(fnM, [a]);
const fnA = useCallback(fnB, [a]);

//参数为[]
父级调用子级时,在onClick参数上加上useCallback,参数为[],则第一次初始化结束后,不在改变。

<ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/>

在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可。而在使用函数的情况,需要考虑有没有函数传递给子组件使用useCallback。而在值有所依赖的项,并且是对象和数组等值的时候而使用useMemo(当返回的是原始数据类型如字符串、数字、布尔值,就不要使用useMemo了)。不要盲目使用这些hooks。

hooks暴露方法属性给父级

//------------  父级 ------------ 
renderToolBar(){
  //获取ref的实例
    return <ToolBarCom ref={ref => (this.ToolBarRef = ref)} />;
}
//调用
  _onScroll(event) {
    this.ToolBarRef && this.ToolBarRef.scrollEvent(event);
    this.changeActivityIcon();
  }


//------------ 子级 ------------ 
//hook暴露这个组件的ref给父级
//1.传入ref  
  const ToolBarCom = (props, ref) => {
//2.暴露属性给父级
 useImperativeHandle(ref, () => ({//第一个参数:暴露哪个ref;第二个参数:暴露什么
    openPullAni: flag => {
      onRefresh();
    },
     value: inputEl.current.value, //属性
       scrollEvent: animEvent,
    }));
  //3.通过forwardRef把组件当作ref传出
export default React.forwardRef(ToolBarCom);

useRef

useRef 返回一个可变的ref对象,其.current属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

function TextInputWithFocusButton() {
    const inputEl: MutableRefObject<any> = useRef(null);
    const onButtonClick = () => {
        // `current` 指向已挂载到 DOM 上的文本输入元素
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}
在组件的整个生命周期里,inputEl.current都是存在的,这扩展了useRef本身的用途,可以使用useRef维护类似于class.component中实例属性的变量。

利用ref保存前一个值.
发生了什么:
//setCount方法调用, 触发userEffect[count], 这个时候给preCount给旧值.
//useRef相当于被初始化为传入的参数(initialValue),不调用 prevCountRef.current =,值不会变.
const Count = () => {
    const [count, setCount] = useState(0);
    const prevCountRef = useRef(count);
    const prevCount = prevCountRef.current;

  
    useEffect(() =>{
      //在setCount执行完成前触发,拿到之前的值
        prevCountRef.current = count;
        console.log('useEffect:', prevCountRef)
    }, [count]);
    
    return (
        <div>
            <button onClick={() => { setCount( count + 1 ) }}>加1</button>
            <p>count: {count}</p>
            <p>prevCount: {prevCount}</p>
        </div>
    )
}


利用ref保存未来的值

function Example() {
  const [count, setCount] = useState(0);
  const currentCount = useRef(count);
  // currentCount.current现在是旧值,因为新值还没有给进去
  const prevCount = currentCount.current;
//count挂载的useState,最新的值.
  currentCount.current = count;

  return (
    <div>
      <p>prev: {prevCount}</p>
      <p>current: {currentCount.current}</p>
      <button onClick={() => setCount(count + 1)}>setCount</button>
    </div>
  );
}

export default Example;

forwardRef

forwardRef,它是react16新增的方法,返回react组件。

import React,{Component} from 'react';
import ReactDOM,{render} from 'react-dom';
const Child = React.forwardRef((props,ref)=>{
    return (
        <div ref={ref}>{props.txt}</div>
    )
})

class Parent extends Component{
    constructor(){
        super();
        this.myChild = React.createRef();
    }
    componentDidMount(){
        console.log(this.myChild.current);//获取的是Child组件中的div元素

    }
    render(){
        return <Child ref={this.myChild} txt="parent props txt"/>
    }

项目里的例子

//父级
  const pullRef = useRef();
  pullRef.current.deleteItem(index);
  /**
   * 渲染整体列表
   * @method _renderList
   * @return {any}
   */
  const _renderList = (() => {
    return <PullListCom
      ref={pullRef}
      renderItem={renderItem}
      onFetch={_onFetch}
      emptyTip={emptyTip}
      emptyHeight={158}
      networkStatus={networkStatus}
    />;
  })();

//子级
export default forwardRef(PullListCom);