วิธีทำให้แอปพลิเคชัน React ของคุณแสดงผลเร็วขึ้น
การส่งมอบแอปพลิเคชันอย่างรวดเร็วเป็นเรื่องง่าย การส่งมอบแอปพลิเคชันที่รวดเร็วนั้นยากกว่ามาก ต่อไปนี้เป็นเคล็ดลับในการทำให้แอปพลิเคชันตอบสนองของคุณทำให้ส่วนประกอบของคุณเร็วขึ้น
ในขณะที่เราพยายามส่งใบสมัครให้เร็วขึ้น เรามักจะลืมให้ความสำคัญกับแนวทางปฏิบัติที่ดีที่สุด หลังจากนั้นไม่นาน เราก็สร้างส่วนประกอบที่ซับซ้อนมาก เมื่อคุณพิมพ์ตำแหน่งที่สำคัญ ไม่จำเป็นต้องแสดงผลก็จะถูกเรนเดอร์ นี่คือรายการสิ่งที่ควรทำและไม่ควรทำ
ใช้ React.Memo
เรามักไม่ต้องการให้คอมโพเนนต์ของเราเรนเดอร์ทุกครั้งที่พาเรนต์เรนเดอร์ เราสามารถพูดได้ว่า React จะไม่เรนเดอร์ส่วนประกอบของเรา เว้นแต่จำเป็น
// Wrong usage
// Without React.memo this component will get rendered each time
// something causes its parent to render
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
export default YourHeavyComponent
// Good usage
// This component will not render unless name, title or onClick changes
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
export default React.memo(YourHeavyComponent)
// This component will not render unless title changes
const YourHeavyComponent = (name, title, onClick) => {
// Render something really heavy like whole page here
}
// This usage might better for some stations, use this second argument
// to specify which props will cause render
export default React.memo(YourHeavyComponent, (prevProps, nextProps) => {
return prevProps.title === nextProps.title
})
ห้ามมีอุปกรณ์ประกอบฉากมากกว่า 4 ชิ้นต่อส่วนประกอบ
การมีอุปกรณ์ประกอบฉากจำนวนมากมักเป็นปัญหาเสมอ อุปกรณ์ประกอบฉากแต่ละชิ้นหมายถึงอีกหนึ่งตัวแปรที่ต้องพิจารณาเมื่อพูดถึงประสิทธิภาพ และจะทำให้โค้ดของคุณอ่านและบำรุงรักษาได้ยากขึ้น แทนที่จะสร้างส่วนประกอบที่มีอุปกรณ์ประกอบฉาก 10 ชิ้น ให้สร้างชิ้นส่วนขนาดเล็ก 3 ชิ้นที่มีอุปกรณ์ประกอบฉาก 3-4 ชิ้น
// this component has too many filters
// it's not a good idea to add filters inside this component
// instead we need to create a seperate component to add filter inputs
// and we can also remove title from here
const List = (title, items, sortBy, keys, filters, onFilterChange, children, pagination) => {
// Render something really heavy like whole page here
}
// here userslist wont rendered unless we get a new list
// pagination payload won't cause rendering in filters
const [payload, setPayload] = useState()
const [list, setList] = useState([])
return <>
<Filters onFilterChange={setPayload} filters={payload.filters} />
<UserList items={list}/>
<Pagination onPaginationChange={setPayload} pagination={payload.pagination} />
</>
hook เมื่อคุณมีตัวแปรที่จะคำนวณหลังจากมีการเปลี่ยนแปลงบางอย่าง มีประโยชน์เพราะหากการคำนวณของคุณต้องใช้เวลาหรือหลังการคำนวณ คุณจะได้รับการอ้างอิงหน่วยความจำที่แตกต่างกันสำหรับอาร์เรย์หรือวัตถุ
โปรดอย่าลืมเมื่อตัวแปรของคุณมีการเปลี่ยนแปลงและคุณส่งต่อไปยังองค์ประกอบย่อย องค์ประกอบย่อยจะถูกแสดงผลอีกครั้ง
// here we use useMemo for purely caching purposes, if we don't use useMemo
// each time this components rendered the calculation will have to re-run
const heavyCalculated = useMemo(() => doSomeHeavyCalculation(variable1), [variable])
// an example of wrong usage
// each time this components get rendered this
// styles variable will point to different memory address
// and this will cause rerender of YourHeavyComponent
const styles = {
container:{
marginLeft: left,
marginRight: right,
marginTop: top,
marginBottom: bottom,
}
}
// correct usage of useMemo
// this will cache your value and its memory point won't change
// so even if this components gets rendered your YourHeavyComponent won't be rendered again
const styles = useMemo(() => ({
container:{
marginLeft: left,
marginRight: right,
marginTop: top,
marginBottom: bottom,
}
}), [left, right, top, bottom])
คุณต้องใช้ useCallback hook เมื่อคุณจะส่งฟังก์ชันของคุณไปยังส่วนประกอบย่อย อย่าลืมว่าฟังก์ชันต่างๆ จะชี้ไปยังจุดหน่วยความจำที่แตกต่างกันในแต่ละครั้งที่ส่วนประกอบของคุณแสดงผล เราจำเป็นต้องระงับการเรียกกลับก่อนหน้าในหน่วยความจำ ดังนั้นมันจะไม่เปลี่ยนแปลงทุกครั้งที่เราทำบางสิ่งในองค์ประกอบหลักและทำให้เกิดการเรนเดอร์ในลูก
// wrong usage
// each time App components get rendered for some reason
// onClickCallback will be recreated and it will point to different memory address
// so it will cause YourHeavyComponent to re render
const onClickCallback = () => {
// do some stuff
}
return <div>
<YourHeavyComponent onClick={onClickCallback} />
</div>
// good usage of onClickCallback
// each time App components if your variable1 don't change
// onClickCallback will point to same memory point
// so UourHeavyCallback won't render.
const onClickCallback = () => {
// do some stuff
}
return <div>
<YourHeavyComponent onClick={onClickCallback} />
</div>
เราคุ้นเคยกับการใช้ hook นี้เพื่อส่งต่อไปยังเด็ก ๆ และใช้อินสแตนซ์ของพวกเขา แต่นี่ไม่ได้มีเพียงกรณีการใช้งานที่มีให้เท่านั้น เราสามารถใช้มันเป็นกลไกการแคชและสถานะบางอย่างโดยไม่ทำให้เกิดการแสดงผลซ้ำ
// in this example we look for useRef value instead of setting
// sent reference is being used for checking if we send this message before or not
// this could hold in the state but if we put into state it will cause render
// it into state, because we don't want to re-render after we set sent.current
const sent = useRef(false)
const postMessage = useCallback(() => {
if (!sent.current) {
// make your api call here
sent.current = true
}
}, [])
อย่าลืมสิ่งนี้ ทุกครั้งที่คุณเรนเดอร์ส่วนประกอบ ตัวแปรภายในส่วนประกอบนั้นจะชี้ที่อยู่หน่วยความจำที่แตกต่างกัน เมื่อมันมาถึงประเภทดั้งเดิมเช่นสตริงและจำนวนเต็ม สิ่งนี้จะไม่ทำให้เกิดปัญหาใด ๆ แต่ถ้าคุณทำงานกับอาร์เรย์ ออบเจกต์ และฟังก์ชัน (เพราะแต่ละฟังก์ชั่นเป็นวัตถุที่อยู่หลังประทุน) มาดูตัวอย่างกัน
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
return <YourHeavyComponent style={{
marginLeft: 10,
marginRight: 10
}} />
}
const style = {
marginLeft: 10,
marginRight: 10
}
// We move styles outside of component
// this way style will point the same memory address regardless of
// how many tames InlineTest gets rendered
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
return <YourHeavyComponent style={style} />
}
const InlineTest = () => {
// the usage below will cause YourHeavyComponent to render if
// something causes InlineTest component to render
// because onClick will be assigned to a new function each time
return <YourHeavyComponent onClick={() => {
console.log('clicked');
}} />;
}
import {useCallback} from "react";
const InlineTest = () => {
// the usage below is correct way to do it
// using useCallback will make sure onClick function
// is the same between renders and it won't cause render in below component
const onClick = useCallback(() => {
console.log('clicked');
}, [])
return <YourHeavyComponent onClick={onClick} />;
}
คุณอาจคิดว่าการจัดการแบบฟอร์มในการตอบสนองเป็นเรื่องง่าย แต่ขอรับประกันว่าไม่เป็นเช่นนั้น เมื่อแบบฟอร์มของคุณซับซ้อนและคุณจำเป็นต้องทำการตรวจสอบ ฯลฯ คุณจะทำให้เกิดการเรนเดอร์มากเกินไป นั่นเป็นเหตุผลว่าทำไมการใช้ห้องสมุดที่ดีจึงมีความสำคัญ สองสามปีที่ผ่านมาฉันลองใช้ไลบรารีต่างๆ มากมาย แต่มีเพียงไลบรารีเดียวที่ควรค่าแก่การกล่าวถึงที่นี่ แบบฟอร์มเบ็ดปฏิกิริยา
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = data => console.log(data);
return (
/* "handleSubmit" will validate your inputs before invoking "onSubmit" */
<form onSubmit={handleSubmit(onSubmit)}>
{/* register your input into the hook by invoking the "register" function */}
<input defaultValue="test" {...register("example")} />
{/* include validation with required or other standard HTML validation rules */}
<input {...register("exampleRequired", { required: true })} />
{/* errors will return when field validation fails */}
{errors.exampleRequired && <span>This field is required</span>}
<input type="submit" />
</form>
);
}