工作中的那点事之 React


工作中的那点事之 React

PropsWithChildren

Props 声明带有 children 的 props。

1
2
3
export type AProps = PropsWithChildren<{
onPress: () => void;
}>;

PropsWithChildren 有可能会类型报错(直接用 vite、react@18.3.1生成的项目没有报错)

如果报错,则需要用一些类型断言。

1
2
3
export const Radio = memo(function Radio(props: RadioProps) {
// ...
}) as (props: RadioProps) => ReactElement;

ref 传参回调

react 开发中,经常会遇到使用 ref 操作 dom 的场景,其中也不乏很简单的操作,比如让 input 聚焦。这种简单的操作就可以直接回调的方式来实现。

一般做法:

1
2
3
4
5
6
7
8
9
10
11
function App() {
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);

return <input ref={inputRef} />;
}

传参回调:

1
2
3
4
5
6
7
function App() {
const refCallback = useCallback((ref: HTMLInputElement) => {
ref.focus();
}, []);

return <input ref={refCallback} />;
}

效果一样。

组件间传递数据

非常基础的常规的 props、回调函数应该没必要啰嗦了。

useImperativeHandleforwardRef

父组件可以通过回调函数的形式获取子组件返回的数据,不过这种形式没有办法在父组件主动获取子组件的数据。
可以使用useImperativeHandle暴露子组件的数据,需要搭配forwardRef使用

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useRef } from "react";
import { Son, SonHandles } from "./Son";

export const Father = () => {
const ref = useRef<SonHandles>(null);
const onClick = (): void => {
console.log(ref.current?.getCount());
};

return (
<>
<Son ref={ref} count={100} />

<div>
<button onClick={onClick}>getSonCount</button>
</div>
</>
);
};

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { ForwardedRef, forwardRef, useImperativeHandle, useState } from "react";

interface SonProps {
count: number;
}

export interface SonHandles {
getCount: () => number;
}

export const Son = forwardRef(
(props: SonProps, ref: ForwardedRef<SonHandles>) => {
const { count: countProp } = props;
const [count, setCount] = useState(countProp);

useImperativeHandle(ref, () => ({
getCount: () => count,
}));

return (
<>
<div>{count}</div>
<button onClick={(): void => setCount(count - 1)}>-1</button>
<button onClick={(): void => setCount(count + 1)}>+1</button>
</>
);
}
);

Context

Context可以实现跨组件传输数据。添加数据用的Context的话,还可以完全不借助第三方的状态库,完全使用React实现。

count-context.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { createContext, ReactNode, useContext } from "react";

export interface CountContextModel {
count: number;
changeCount: (count: number) => void;
}

const CountContext = createContext<CountContextModel>({
get count(): never {
throw new Error("count is not implemented");
},
get changeCount(): never {
throw new Error("changeCount is not implemented");
},
});

export interface CountProviderProps {
children: ReactNode;
value: CountContextModel;
}

export const CountProvider = (props: CountProviderProps) => {
const { children, value } = props;
return (
<CountContext.Provider value={value}>{children}</CountContext.Provider>
);
};

export const useCount = () => {
return useContext(CountContext).count;
};

export const useChangeCount = () => {
const { changeCount } = useContext(CountContext);

return (count: number) => {
changeCount(count);
};
};

App.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useState } from "react";
import { CountProvider } from "./context/count-context";
import { Comp1 } from "./components/Comp1";
import { Comp2 } from "./components/Comp2";

export default function App() {
const [count, setCount] = useState(100);

return (
<CountProvider value={{ count, changeCount: setCount }}>
<Comp1 />
<Comp2 />
</CountProvider>
);
}

Comp1.tsx

1
2
3
4
5
6
7
8
9
10
11
12
import { useCount } from "../context/count-context";

export const Comp1 = () => {
const count = useCount();

return (
<div>
comp1
<div>{count}</div>
</div>
);
};

Comp2.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useChangeCount, useCount } from "../context/count-context";

export const Comp2 = () => {
const count = useCount();
const changeCount = useChangeCount();

return (
<div>
comp2
<div>{count}</div>
<button onClick={(): void => changeCount(count - 1)}>-1</button>
<button onClick={(): void => changeCount(count + 1)}>+1</button>
</div>
);
};

Render Props

组件的children属性可以接收一个函数,该函数的参数为组件的内部状态,并返回 React 元素。

通过这种方式,可以将状态与渲染解耦,状态管理在组件内部,而渲染逻辑交给外面处理。可以在多个地方复用逻辑,即使 UI 差别很大。

demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { memo, ReactElement, useState } from "react";

interface ComponentProps {
children: (count: number) => React.ReactElement;
}

const Component = memo((props: ComponentProps) => {
const { children } = props;
const [count, setCount] = useState(0);

return (
<div>
{children(count)}
<button onClick={(): void => setCount(count + 1)}>+1</button>
</div>
);
});

export default function Home() {
return (
<>
<Component>
{(count): ReactElement => {
return <div>{count}</div>;
}}
</Component>
</>
);
}

文章作者: 赤蓝紫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 赤蓝紫 !
评论
  目录