const a = 1;
const b = 1;
const addNum = () => {
return a + b;
};
const addNum = (a: number, b: number) => {
return a + b;
};
bad示例延长了函数addNum外部变量a和b的作用域链,不利于垃圾回收机制尽快回收a和b;
延长了代码阅读的链路;
不利于复用addNum函数;
不够纯,有副作用;
不够内聚;
{isShow ? <div>显示内容</div> : null}
.displayed {
display: block;
}
.hidden {
display: none;
}
CSS 中的 display 条件渲染对性能影响较小,因为只需要在样式表中设置一个属性即可,不需要执行额外的代码来控制元素的显示或隐藏。
const onClick = (a: number) => {
if (number < 10) {
return number;
}
return number + 1;
};
const onClick = (a: number) => (number < 10 ? number : number + 1);
const onClick = (a: number) => (number < 10 ? number : number + 1);
尾调用指的是一个函数在最后一步调用另一个函数,并将其结果返回。
然后,调用函数没有必要保留任何本地状态,因为返回值就是它自己的返回值。
通过这种方式,尾调用可以优化递归算法,避免出现栈溢出的情况。
const addNum = ()=>{...};
<Component onClick={addNum} />
const addNum = ()=>{...}
<Component addNum={addNum} />
bad的写法会出现道具下钻陷阱,使代码难以阅读和理解,如果在子组件里面接收该方法,可能子组件的方法名与props传递下来的方法名不一致,代码阅读的链路和复杂性增加了
一旦porps的属性被传递2层以上,就得考虑设计的是否不够合理
const Header = ()=>{
const data = [1,2,3,4];
return <>
{data.filter((d)=>d>2).map((d)=><span key={d}>{d}</span>)}
</>
}
const Header = ()=>{
const data = [1,2,3,4].filter((d)=>d>2);
return <>
{data.map((d)=><span key={d}>{d}</span>)}
</>
}
why
UI渲染应该与数据逻辑处理分离,减少耦合
不要维护与父组件渲染无关的状态
import React, { useState } from "react";
interface ModalProps {
open: boolean;
setOpen: (v: boolean) => void;
otherProps: any;
}
const Modal = (props: ModalProps) => {
return <>根据父组件传递下来的open和setOpen决定是否要打开和关闭Modal</>;
};
const FatherComponent = () => {
const [open, setOpen] = useState(false);
const [otherProp, setOtherProp] = useState("");
return (
<>
<span
onClick={() => {
// 可能存在这样的业务,触发某个事件需要将最新的otherProps修改,并传给Modal
setOtherProp("");
}}
>
父组件其他的业务逻辑代码
</span>
<Modal open={open} setOpen={setOpen} otherProps={otherProp} />
</>
);
};
export default FatherComponent;
import React, { forwardRef, useImperativeHandle, useRef, useState } from "react";
interface ModalProps {
// 由于对外暴露了组件自己的实例,所以基本上不需要接收props,父组件与子组件的耦合度降低
}
interface ModalRef {
show: (otherProps: any) => void;
close: () => void;
}
export const useModalRef = () => useRef<ModalRef>(null);
const Modal = forwardRef<ModalRef, ModalProps>((props, ref) => {
const [otherProps, setOtherProp] = useState("");
const [open, setOpen] = useState(false);
useImperativeHandle(ref, () => ({
show: (v: any) => {
setOtherProp(v);
setOpen(true);
},
close: () => {
setOpen(false);
},
}));
return (
<>
打不打开Modal{open}、怎么储存{otherProps}的能力被自己回收,父组件只能调用自己的实例触发
</>
);
});
const FatherComponent = () => {
const modalRef = useModalRef();
return (
<>
<span
onClick={() => {
// 父组件再也不用维护与自身业务无关的,本该属于子组件管理的状态——otherProps
modalRef.current?.show("otherProps");
}}
>
父组件其他的业务逻辑代码
</span>
<Modal ref={modalRef} />
</>
);
};
export default FatherComponent;
why
bad示例如果有更多的其他业务modal处理,并且也需要根据父组件的某些业务更新子组件的状态,父组件将变得越来越臃肿,有两个modal就得存两份open、存两份otherProps,这对于通用业务组件的封装和抽离将是灾难
good示例利用对外暴露实例的方式解耦了父组件与子组件状态的维护和管理逻辑,将状态通过调用组件实例的方式传给了子组件,如果有新的业务扩展,子组件完全有能力继续内聚继续扩展实例方法和属性。
但是bad示例将会在父组件维护数不清的本该属于子组件的状态和方法。