even

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1、useImperativeHandle的用法

当使用 useImperativeHandle 时,你通常会与 forwardRef 一起使用,以允许你暴露一些子组件内部的方法或属性,以便父组件可以直接访问。以下是一个使用 TypeScript 的例子,演示如何在子组件内使用 useImperativeHandle

首先,创建一个子组件,命名为 ChildComponent,该组件将使用 forwardRefuseImperativeHandle 来暴露一个 focus 方法:

import React, { forwardRef, useImperativeHandle, Ref } from 'react';

interface ChildComponentProps {}

export interface ChildComponentRef {
  focus: () => void;
}

const ChildComponent = forwardRef<ChildComponentRef, ChildComponentProps>(
  (props, ref): React.ReactElement => {
    useImperativeHandle(ref, () => ({
      focus() {
        console.log('ChildComponent focused');
        // Place the actual focus logic here
      },
    }));

    return <div>This is the ChildComponent</div>;
  }
);

export default ChildComponent;

然后,在父组件中,你可以使用 ref 来调用子组件暴露的 focus 方法:

import React, { FC, useRef } from 'react';
import ChildComponent, { ChildComponentRef } from './ChildComponent';

const App: FC = (): React.ReactElement => {
  const childRef = useRef<ChildComponentRef | null>(null);

  const handleFocus = () => {
    if (childRef.current) {
      childRef.current.focus();
    }
  };

  return (
    <div>
      <h1>This is the App</h1>
      <ChildComponent ref={childRef} />
      <button onClick={handleFocus}>Focus Child Component</button>
    </div>
  );
};

export default App;

在这个例子中,ChildComponent 使用了 forwardRefuseImperativeHandle,以允许父组件通过 ref 来调用子组件的 focus 方法。父组件 App 利用了 childRef 来获取 ChildComponent 的引用,并通过按钮点击触发 handleFocus 函数来调用子组件的 focus 方法。

2、useTransition

官方解释:返回一个状态值表示过渡任务的等待状态, 以及一个启动该过渡任务的函数

通俗的讲,就是告诉react对于某部份任务的更新优先级较低,可以稍后更新(这是一个性能优化工具)

import {
  BaseSyntheticEvent,
  FC,
  ReactElement,
  useState,
  useTransition,
} from 'react';

const list = new Array(10000).fill('1').map((item, index) => index.toString());

const App: FC = (): ReactElement => {
  const [numbers, setNumbers] = useState<string[]>(list);
  const [padding, setTransition] = useTransition();
  const inputChangeEvent = (event: BaseSyntheticEvent) => {
    /**
     * 相当于每次进行更改的时候,视图需要用时间去渲染,这个时候会阻塞页面的输入
     * 而把逻辑放到setTransition的回调中,相当于是不阻塞页面的输入,页面后台进行操作
     * 并且返回一个padding给我们,当为true的时候表示视图更新未完成
     * 以上就是useTransition的用法(为了更好的展示,可以把页面调试工具中的Performance的cpu调慢4倍)
     */
    
    setTransition(() => {
      const val = event.target.value;
      setNumbers(list.filter((item) => item.includes(val)));
    });
  };

  return (
    <div>
      <input type="text" onInput={inputChangeEvent} />
      {padding && <div>视图正在刷新中。。。。。</div>}
      <div>
        {numbers.map((item) => (
          <div key={item}>{item}</div>
        ))}
      </div>
    </div>
  );
};

export default App;

useDeferredValue

这个api的功能相当于useTransition,使用起来更方便,但是没有提供状态的变量

import {
  BaseSyntheticEvent,
  FC,
  ReactElement,
  useDeferredValue,
  useState,
} from 'react';

const list = new Array(10000).fill('1').map((item, index) => index.toString());

const App: FC = (): ReactElement => {
  const [numbers, setNumbers] = useState<string[]>(list);

  // 这个方法提供的功能与useTransition一样,就是一个延迟渲染,但是这个没有提供padding这个变量
  const deferNumbers = useDeferredValue(numbers);

  const inputChangeEvent = (event: BaseSyntheticEvent) => {
    const val = event.target.value;
    setNumbers(list.filter((item) => item.includes(val)));
  };

  return (
    <div>
      <input type="text" onInput={inputChangeEvent} />
      <div>
        {deferNumbers.map((item) => (
          <div key={item}>{item}</div>
        ))}
      </div>
    </div>
  );
};

export default App;

3、自定义hook

当我们想在两个函数之间共享逻辑时, 我们会把它提取到第三个函数中,而组件和Hook都是函数,所以也同样适用这种方式

自定义hook是一个函数, 其名称是以use开头, 函数内部可以调用其他的hook

import React, {useEffect, useState} from 'react';
import ReactDom from 'react-dom'

const useTimer = () => {  //注意:这里的函数是需要以use开头
    let [count, setCount] = useState(0)

    useEffect(() => {   //相当于componentDidMount
        let timer = setInterval(() => {
            setCount(count => ++ count)
        }, 1000)
        return () => {  //相当于componentWillUnmount
            clearInterval(timer)
        }
    }, [])
    return count
}

const CounterOne = () => {
    // let [count, setCount] = useState(0)
    //
    // useEffect(() => {
    //     let timer = setInterval(() => {
    //         setCount(count => ++ count)
    //     }, 1000)
    //     return () => {
    //         clearInterval(timer)
    //     }
    // }, [])
    let count = useTimer()
    return <h2>CounterOne ---{count}</h2>
}

const CounterTwo = () => {
    // let [count, setCount] = useState(0)
    //
    // useEffect(() => {
    //     let timer = setInterval(() => {
    //         setCount(count => ++ count)
    //     }, 1000)
    //     return () => {
    //         clearInterval(timer)
    //     }
    // }, [])
    let count = useTimer()
    return <h3>CounterTwo ---{count}</h3>
}

const App = () => {
    return <div>
        <CounterOne/>
        <CounterTwo/>
    </div>
}

ReactDom.render(<App/>, window.root)

原本useEffect与useState是不能放在函数里,如果放在use开头的函数里,系统会认为是自定义的hook,这样就可以对对应的方法进行封装和使用

 案例2: 对dispatch进行扩充,实现改变前与改变后值的打印

import React, {useEffect, useReducer} from 'react';
import ReactDom from 'react-dom'

const useLogger = () => {
    const reducer = (state, config) => {
        if(config.type === 'count') {
            return {
                ...state,
                count: state.count + config.step
            }
        } else if(config.type === 'name') {
            return {
                ...state,
                name: config.target
            }
        }
    }
    let [state, dispatch] = useReducer(reducer, {name: 'yfbill', count: 0})
    const actions = (config) => {       //对原有dispatch进行扩充
        console.log('before', state)
        dispatch(config)
    }
    useEffect(() => {
        console.log('after', state)
    })
    return [state, actions] //输出的扩充后的dispatch

}

const Item = () => {
    let [state, actions] = useLogger()
    return <div>
        <h3>this is Item---name: {state.name} --- count: {state.count}</h3>
        <button onClick={() => { actions({type: 'count', step: 1})}}>count++</button>
        <button onClick={() => { actions({type: 'name', target: 'aaaa'})}}>changeName</button>
    </div>
}

const App = () => {
    return <div>
        <h2>this is App</h2>
        <Item/>
    </div>
}

ReactDom.render(<App/>, window.root)

注意:以上这个嵌入式的写法可以用作数据的请求,方便封装操作 

 4、高阶组件的写法

react的高阶组件是通过在组件外添加一层函数,对组件进行劫持,进行一些数据的注入或者其他操作的一个行为

例如利用高阶组件注入相关的用户信息

高阶组件:

import { ComponentType, ReactElement } from 'react';
import {
  IUserInfoContextProp,
  UserInfoContext,
} from '../context/UserInfoContext';

const withInfo = <T,>(OriginComponent: ComponentType<T>): ComponentType<T> => {
  return (props: T): ReactElement => {
    return (
      <UserInfoContext.Consumer>
        {(value: IUserInfoContextProp) => (
          <OriginComponent {...props} {...value} />
        )}
      </UserInfoContext.Consumer>
    );
  };
};

export default withInfo;

子组件举例

import { PureComponent, ReactElement } from 'react';
import withInfo from '../hooks/withInfo';

class Header extends PureComponent<{}> {
  public render(): ReactElement {
    console.log(this.props);
    return <div>header</div>;
  }
}

export default withInfo(Header);

父组件调用

import { FC, ReactElement } from 'react';
import { UserInfoContext } from './context/UserInfoContext';
import Header from './components/Header';

const userInfo = {
  name: 'even',
  age: 12,
};

const App: FC<{}> = (): ReactElement => {
  return (
    <div>
      <UserInfoContext.Provider value={userInfo}>
        <h1>this is app</h1>
        <Header />
      </UserInfoContext.Provider>
    </div>
  );
};

export default App;

 

posted on 2021-04-06 12:21  even_blogs  阅读(1501)  评论(0编辑  收藏  举报