cloud setups
- TypeScript Playground with React just if you are debugging types (and reporting issues), not for running code
- CodeSandbox - cloud IDE, boots up super fast
- Stackblitz - cloud IDE, boots up super fast
组件的写法
function
function Hello({ ame }: Props) {
return (
<div>Hello, {name}</div>
);
}
export default Hello;
type ButtonProps = {
text: string
}
const Button: React.FC<ButtonProps> = ({ text }) => {
return <button>{text}</button>
}
export default Button
class
class Hello extends React.Component<Props, object> {
render() {
const { name } = this.props;
return (
<div>Hello, {name}</div>
);
}
}
函数式组件和类组件的不同
React Hooks由于是函数式组件,在异步操作或者使用useCallBack、useEffect、useMemo等API时会形成闭包。
Hooks
1. useState
function Counter({initialCount}) {
const [count, setCount] = useState<nubmer>(initialCount);
return (
<> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> </>
);
}
2. useReducer
reducer: (state, action) => newState
适用情况:
- state 逻辑较复杂且包含多个子值
- 下一个 state 依赖于之前的 state
- 会触发深更新的组件做性能优化,因为你可以向子组件传递
dispatch
而不是回调函数 。
const initialState = { count: 0 };
type ACTIONTYPE =
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string };
function reducer(state: typeof initialState, action: ACTIONTYPE) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "decrement", payload: "5" })}>
-
</button>
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
+
</button>
</>
);
}
惰性初始化
将 init
函数作为 useReducer
的第三个参数传入,这样初始 state 将被设置为 init(initialArg)
。
import * as React from "react";
import { useReducer } from "react";
const initialCount = 0;
type ACTIONTYPE =
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string }
| { type: "reset"; payload: number };
function init(initialCount: number) {
return { count: initialCount };
}
function reducer(state: {count: number}, action: ACTIONTYPE) {
switch (action.type) {
case "reset":
return init(action.payload);
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
export function Counter() {
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", payload: "5" })}>
-
</button>
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
+
</button>
</>
);
}
3. useEffect
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
useEffect(() => {
setTimeout(() => {
/* do stuff */
}, timerMs);
}, [timerMs]);
// better; use the void keyword to make sure you return undefined
return null;
}
4. useRef
const ref1 = useRef<HTMLElement>(null!);
const ref2 = useRef<HTMLElement>(null);
const ref3 = useRef<HTMLElement | null>(null);
useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内保持不变。
function TextInputWithFocusButton() {
// initialise with null, but tell TypeScript we are looking for an HTMLInputElement
const inputEl = React.useRef<HTMLInputElement>(null);
const onButtonClick = () => {
// strict null checks need us to check if inputEl and current exist.
// but once current exists, it is of type HTMLInputElement, thus it
// has the method focus! ✅
if (inputEl && inputEl.current) {
inputEl.current.focus();
}
};
return (
<>
{/* in addition, inputEl only can be used with input elements. Yay! */}
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
5. useContext
const value = useContext(MyContext);
接收一个 context 对象(React.createContext
的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value
prop 决定。
6. Custom Hooks
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[]
}
也可以写成:
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as [
boolean,
(aPromise: Promise<any>) => Promise<any>
];
}
上下文(Context)
上下文(Context) 提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性。
使用 Context 可能比替代方案更简单的常见示例包括管理当前本地设置,主题或数据缓存。
在使用 Context 之前
当一些数据需要在不同的嵌套级别上被_许多_组件访问时,首先考虑使用 Context 。 请谨慎使用它,因为它使组件重用更加困难。
API
- 通过 createContext 创建一个名为 color 的 context
- 通过 Provider 的 value 属性传值
- 通过 Consumer 的 props 接收值
React.Children.only
验证 children
是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。
React.Children.only(children)
ReactNode
ReactNode 是虚拟 DOM 的基本构建,可以是以下任意一种核心类型
- ReactElement
它是 React 中的基础类型,是 DOM 中 Element 的一种轻量、无状态、不可变的虚拟表现形式
- ReactText
它是一个数字或者字符串,它代表了文本内容,是 DOM 中的文本节点的虚拟表示形式
ReactElement 和 ReactText 都是 ReactNode 。 一个 ReactNode 的数组称为 ReactFragment 。
定义事件处理程序
const App = () => {
const handleClick: React.MouseEventHandler<HTMLButtonElement> = e => {
console.log(e)
}
return <button onClick={handleClick}>Click</button>
}
- Event Handler
我们必须先选择这是要处理一下Event的Handler,Event种类有很多,都是以Event结尾,像是MouseEvent,FormEvent,KeyboardEvent等等,可参考React官方文件 Target Element
接下来我们必须定义Handler的作用目标是什么,以示例来说,我们要处理的是作用在“按钮”上面的点击事件,所以我们必需要使用HTMLButtonElement
来宣告我们的作用目标,作用目标有很多种,都是HTML开头,Element结尾,像是HTMLFormElement,HTMLDivElement等等,可参考MDN
React.forwardRef
父组件操作子组件DOM,接收一个函数组件,这个函数组件可以接收ref参数React.forwardRef<T, P = {}>
只需要传props
的类型和ref
的类型,第一个T
是ref
的类型,P
是props
的类型
React.ForwardRefRenderFunction
定义为该类型的函数可以放进React.forwardRef
函数中作为参数
const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (props, ref) => {
const buttonNode = (
<button
{...(rest as NativeButtonProps)}
type={htmlType}
className={classes}
onClick={handleClick}
ref={buttonRef}
>
{iconNode}
{kids}
</button>
);
if (isUnborderedButtonType(type)) {
return buttonNode;
}
return <Wave>{buttonNode}</Wave>;
};
const Button = React.forwardRef<unknown, ButtonProps>(InternalButton) as CompoundedComponent;
Partial
将类型定义的所有属性都修改为可选。
type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>;
// 等价于
type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>;
type ButtonProps = {
AnchorButtonProps?: typeof AnchorButtonProps;
NativeButtonProps?: typeof NativeButtonProps;
}
Enum 类型
尽量避免使用枚举类型,可以用以下代替:
export declare type Position = "left" | "right" | "top" | "bottom";
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。