创建React元素
创建 React 元素可以通过 JSX 扩展语法 或者 createElement api 两种方式。
jsx 标签
zh-hans.react.dev/learn/writi…
JSX是对HTML标签的一种语法扩展,JSX 语法更加严格并且相比 HTML 有更多的规则,开发组件时一般将渲染逻辑和标签存在同一个地方-组件。
function Greeting({ name }) {
return (
<h1 className="greeting">
你好<i>{name}</i>,欢迎!
</h1>
);
}
export default function App() {
return <Greeting name="泰勒" />;
}
createElement
如果不喜欢JSX,可以使用 createElement 作为替代方案。createElement返回一个React 元素。
API详情:createElement(type, props, ...children)
import { createElement } from 'react';
function Greeting({ name }) {
return createElement(
'h1',
{ className: 'greeting' },
'你好',
createElement('i', null, name),
'。欢迎!'
);
}
export default function App() {
return createElement(Greeting, { name: '泰勒' });
}
React 元素
React 元素是用于描述用户界面的轻量级结构。
类似于
{
type: Greeting,
props: {
name: '泰勒'
},
key: null,
ref: null,
}
创建这个对象不会渲染组件或创建DOM元素,它告诉React如何渲染组件。
React内置类型
React.ReactElement
React.ReactElement 是一个接口,对应了一个对象的结构,包括 type、props、key 三个属性值。 通常情况下,函数组件会返回 React.ReactElement 类型的值。
type Key = string | number
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
JSXElementConstructor
JSXElementConstructor 表示一个函数式组件 或 类式组件。
type JSXElementConstructor<P> =
| ((props: P) => ReactElement<any, any> | null)
| (new (props: P) => Component<any, any>);
JSX.Element
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
}
}
可以看出 JSX.Element 继承自React.ReactElement<any, any>。JSX 是一个全局的namespace。 该类型的变量值只能是 ReactElement 实例。 JSX.Element 可以通过执行 React.createElement 或是转译 JSX 获得:
const jsx = <div>hello</div>
const ele = React.createElement("div", null, "hello");
React.ReactNode
ReactNode 是一个联合类型,可以是 string、number、ReactElement、ReactFragment、boolean、null、undefined、或者是ReactNode的数组集合。 因此,ReactElement类型的变量可以赋值给ReactNode类型的变量,反之不行。
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
React Hooks
useRef
在函数组件的顶层使用useRef函数后,会返回一个仅有一个current属性的可变对象,current对象是我们设置的初始值。
下面看下定义:
- 第一种定义
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
* of the generic argument.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
// 第一种类型定义:返回一个可变对象
function useRef<T>(initialValue: T): MutableRefObject<T>;
从第8号代码可以看出,如果希望返回一个可变对象,可以在泛型中添加null。 比如:
const inputRef = useRef<HTMLInputElement|null>(null); // 将返回一个可变对象
- 第二种定义
// convenience overload for refs given as a ref prop as they typically start with a null value
// 第二种类型定义:返回一个不可变对象
// 重载:下面这种使用方式,将返回一个不可变的引用对象,也就是返回的对象的current属性是只读的
function useRef<T>(initialValue: T|null): RefObject<T>;
const inputRef = useRef<HTMLInputElement>(null); // 将返回一个不可变对象
- 第三种定义
// convenience overload for potentially undefined initialValue / call with 0 arguments
// has a default to stop it from defaulting to {} instead
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
const inputRef = useRef(); // 将返回一个可变对象
- 可变对象和不可变对象
// 可变对象
interface MutableRefObject<T> {
current: T;
}
// 不可变对象
interface RefObject<T> {
readonly current: T | null;
}
useState
/**
* Returns a stateful value, and a function to update it.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
// convenience overload when first argument is omitted
/**
* Returns a stateful value, and a function to update it.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
从useState的类型定义可以看出有两种形式,一种是有初始值,一种是没有初始值。useState会返回状态值以及一个更新函数。更新函数的定义如下:
Dispatch表示setState的类型,是一个没有返回值的方法,SetStateAction定义了setState的入参类型,是S或者返回S类型值的一个函数。
type Dispatch<A> = (value: A) => void;
// Unlike the class component setState, the updates are not allowed to be partial
type SetStateAction<S> = S | ((prevState: S) => S);
forwardRef
function forwardRef<T, P = {}>(render: ForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
/** Ensures that the props do not include ref at all */
type PropsWithoutRef<P> =
// Pick would not be sufficient for this. We'd like to avoid unnecessary mapping and need a distributive conditional to support unions.
// 条件类型
// see: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
// https://github.com/Microsoft/TypeScript/issues/28339
P extends any ? ('ref' extends keyof P ? Pick<P, Exclude<keyof P, 'ref'>> : P) : P;
interface RefAttributes<T> extends Attributes {
ref?: Ref<T> | undefined;
}
// will show `ForwardRef(${Component.displayName || Component.name})` in devtools by default,
// but can be given its own specific name
interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> {
defaultProps?: Partial<P> | undefined;
propTypes?: WeakValidationMap<P> | undefined;
}
interface NamedExoticComponent<P = {}> extends ExoticComponent<P> {
displayName?: string | undefined;
}
// TODO: similar to how Fragment is actually a symbol, the values returned from createContext,
// forwardRef and memo are actually objects that are treated specially by the renderer; see:
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31
// However, we have no way of telling the JSX parser that it's a JSX element type or its props other than
// by pretending to be a normal component.
//
// We don't just use ComponentType or FunctionComponent types because you are not supposed to attach statics to this
// object, but rather to the original function.
interface ExoticComponent<P = {}> {
/**
* **NOTE**: Exotic components are not callable.
*/
(props: P): (ReactElement|null);
readonly $$typeof: symbol;
}
interface ForwardRefRenderFunction<T, P = {}> {
(props: PropsWithChildren<P>, ref: ForwardedRef<T>): ReactElement | null;
displayName?: string | undefined;
// explicit rejected with `never` required due to
// https://github.com/microsoft/TypeScript/issues/36826
/**
* defaultProps are not supported on render functions
*/
defaultProps?: never | undefined;
/**
* propTypes are not supported on render functions
*/
propTypes?: never | undefined;
}
ForwardRefRenderFunction和ForwardRefExoticComponent之前的区别