This artical will display the concept of react
ReactNode vs ReactElement
let's see the code definition. ReactNode is a superset of ReactElement, ReactElement is an object with type, props, key
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
Order when React updating the virtual Dom.
See the code Sample bellow. Assume current State is 1, and we Click button, state will be setted to 2, then in RenderDemo, it will first update 2 to InnerInner, but function is not executing, then executing Inner, InnerInner by order.
After also checking the class componnet, I found order is, in render function, first it will assign values to every component(either class or function), then it will execute render in each component iterate, one by one. Before any component'render function executing or any lifecycle methods executed, all properties and children has already been updated in parent component so the order in bellow example is innerFunction, innerFunction, innercomponnet, innerInnerComponnet
const Inner:React.FC=({children})=>{
return (
<div>{children}</div>
);
}
const InnerInner=React.forwardRef<HTMLDivElement,any>(({children},ref)=>{
return (
<>
<div ref={ref}>{children}</div>
</>
);
});
interface PropsType{
value:number;
children?:any;
}
class InnerComponnet extends React.Component<PropsType>{
static getDerivedStateFromProps(props:PropsType,state:any){
console.log(`InnerComponent is ${props.value}`);
const childValue = props.children.props.value;
console.log(`in InnerComponent, the child props is ${childValue}`)
}
constructor(props:PropsType){
super(props);
this.state={};
}
render(){
console.log(`render in InnerComponnet`);
return (
<div>{this.props.children}</div>
)
}
}
class InnerInnerComponnet extends React.Component<PropsType>{
constructor(props:PropsType){
super(props);
this.state={};
}
static getDerivedStateFromProps(props:PropsType,state:any){
console.log(`InnerInnerComponent is ${props.value}`);
}
render(){
console.log(`render in InnerInnerComponnet`);
return (
<div>{this.props.value}</div>
)
}
}
const RenderDemo:React.FC = ()=>{
const [state,setState]=React.useState<number>(0);
return(
<>
<div className="each-example">
<h2>Render Order Demo</h2>
<Button onClick={()=>{setState(pre=>pre+1);console.log(`pre is ${state}`);}}>Click to increase</Button>
<Inner>
<InnerInner>{state}</InnerInner>
</Inner>
<h2>This is class Componnet</h2>
<InnerComponnet value={state}>
<InnerInnerComponnet value={state}/>
</InnerComponnet>
</div>
</>
);
}
下面列出几个问题,然后慢慢给出解答
- FC 如果当普通函数用,同时 FC 里面有 useRef/useMemo 等 hook 函数,那么 hook 函数还能正常工作吗?
答案是如果 FC 当作普通函数用,那么 FC 跟 hook 函数是一样的行为,它里面的 useRef/useMemo 等数据存储在调用 FC 的组件内部。很有意思。还有,React 指出 hook api(原生的 API) 函数必须要在最顶层(这其实是一个静态的语法检查),不能置于任何条件以及块语句内部,实际上,我们可以使用自定义的 hook 函数绕过去,我们只要保证函数运行过程中 hook 函数的数量保持不变就行了。下面来个简单的例子
const FCC1: React.FC<{ content: string; count: number; isOverride?: boolean }> =
({ content, count, isOverride }) => {
const text = React.useRef<string>(content);
if (isOverride) {
text.current = content;
}
return <span>{`Conent is ${text.current} and count is ${count}`}</span>;
};
const FCC2 = ({
content,
count,
isOverride,
}: demoState & { isOverride?: boolean }) => {
const text = React.useRef<string>(content);
if (isOverride) {
text.current = content;
}
return text.current;
};
const FCDemo: React.FC = () => {
const [state, setState] = React.useState<demoState>({
content: "a",
count: 0,
});
const onincrease = () => {
setState((pre) => ({ content: `${pre.content}a`, count: pre.count + 1 }));
};
const { content, count } = state;
let fccResult;
if (count % 2 === 0) {
fccResult = FCC1(state);
} else {
const test = FCC2({ ...state, isOverride: true });
fccResult = (
<span>{`I am from local content is ${test} count is ${count}`}</span>
);
}
return (
<>
<button type="button" onClick={onincrease}>
Click ToChange
</button>
<h3>
content update when count is
even,下面其实是两个不同的FC,它们共享了同一个槽位,所以React没有报错,证明就是变化是a,aa,aa,aaaa,aaaa,aaaaaa,如果是不同槽位,应该是a,aa,a,aaaa,a,aaaaaa
</h3>
{fccResult}
</>
);
};
- 研究一下 hook 函数的源码,了解它的实现原理 useMemo, useRef,这两个简单点先来。