1、useImperativeHandle的用法
当使用 useImperativeHandle
时,你通常会与 forwardRef
一起使用,以允许你暴露一些子组件内部的方法或属性,以便父组件可以直接访问。以下是一个使用 TypeScript 的例子,演示如何在子组件内使用 useImperativeHandle
:
首先,创建一个子组件,命名为 ChildComponent
,该组件将使用 forwardRef
和 useImperativeHandle
来暴露一个 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
使用了 forwardRef
和 useImperativeHandle
,以允许父组件通过 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;