วิธีทำให้แอปพลิเคชัน React ของคุณแสดงผลเร็วขึ้น

Nov 27 2022
การส่งมอบแอปพลิเคชันอย่างรวดเร็วเป็นเรื่องง่าย การส่งมอบแอปพลิเคชันที่รวดเร็วนั้นยากกว่ามาก ต่อไปนี้เป็นเคล็ดลับในการทำให้แอปพลิเคชันตอบสนองของคุณทำให้ส่วนประกอบของคุณเร็วขึ้น

การส่งมอบแอปพลิเคชันอย่างรวดเร็วเป็นเรื่องง่าย การส่งมอบแอปพลิเคชันที่รวดเร็วนั้นยากกว่ามาก ต่อไปนี้เป็นเคล็ดลับในการทำให้แอปพลิเคชันตอบสนองของคุณทำให้ส่วนประกอบของคุณเร็วขึ้น

ในขณะที่เราพยายามส่งใบสมัครให้เร็วขึ้น เรามักจะลืมให้ความสำคัญกับแนวทางปฏิบัติที่ดีที่สุด หลังจากนั้นไม่นาน เราก็สร้างส่วนประกอบที่ซับซ้อนมาก เมื่อคุณพิมพ์ตำแหน่งที่สำคัญ ไม่จำเป็นต้องแสดงผลก็จะถูกเรนเดอร์ นี่คือรายการสิ่งที่ควรทำและไม่ควรทำ

ใช้ 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>
  );
}