نکات بهینه‌سازی React

ترجمه و تالیف : عرفان کاکایی
تاریخ انتشار : 13 خرداد 98
خواندن در 2 دقیقه
دسته بندی ها : react

در React، یک کامپوننت وقتی که state یا propهای مربوط به آن تغییر می‌کنند، مجددا رندر می‌شود. با توجه به این که state نمایانگر داده‌های داخلی، و propها نمایانگر داده‌های خارجی هستند، این اتفاق با عقل جور در میاید، اما همیشه موقعیت به این صورت نیست. برخی تکنیک‌ها وجود دارند که از رندرهای غیر ضروری کامپوننت جلوگیری می‌کنند. بیایید برخی مثال‌های موجود را بررسی کنیم.

همیشه مجددا رندر کنید

let count = 0;

class Button extends React.Component {
  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

نمونه کد آنلاین

هر زمان که ما بر روی دکمه‌ها (یا red و یا green) کلیک می‌کنیم، count افزایش می‌یابد، که نمایانگر رندر شدن مجدد کامپوننت Button است. گرچه، تنها prop که Button به آن وابستگی دارد، color است. اگر color همینطور به تغییر یافتن ادامه دهد، رندر مجدد غیر ضروری خواهد بود.

هدف: اگر ما همینطور دکمه red را فشار دهیم، می‌خواهیم که count بدون تغییر بماند؛ زیرا با توجه به این که color همانطور باقی می‌ماند، ما نمی‌خواهیم کامپوننت Button را مجددا رندر کنیم.

تکنیک ۱: shouldComponentUpdate

let count = 0;

class Button extends React.Component {
  shouldComponentUpdate(nextProps) {
    if(this.props.color === nextProps.color) {
      return false;
    }
    return true
  }

  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

نمونه کد shouldComponentUpdate

تکنیک ۲: React.PureComponenet

نحوه کار آن

let count = 0;

class Button extends React.PureComponent {
  render = () => {
    const { color } = this.props
    count++;
    return (
      <React.Fragment>
        <span> Render count: { count } </span>
        <button style={{ color }}> text </button>
      </React.Fragment>
    )
  }
}

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

نمونه کد PureComponent

یک راه حل همه جانبه برای این مشکل

PureComponent اساسا همان Component است که هوک shouldComponentUpdate را به همراه دارد. پس آیا ما باید با توجه به این که PureComponent بهینه‌سازی رندر کردن بومی را در خود دارد، در هر صورت از آن استفاده کنیم؟ نه. PureComponent از برابری سطحی برای مقایسه propها و state استفاده می‌کند.

class TodoList extends React.PureComponent {
  render() {
    const { todos } = this.props;
    return (<ul>
      {
        todos.map((it, index) => <li key={index}> {it} </li>)
      }
    </ul>)
  }
}

class App extends React.Component {
  inputRef = React.createRef()

  state = {
    todos: []
  }

  onAdd = () => {
    let { todos } = this.state;
    const text = this.inputRef.current.value;
    todos.push(text);
    this.setState({
      todos
    });
  }

  render() {
    const { todos } = this.state;
    const { onAdd, inputRef } = this
    return (
      <React.Fragment>
        <input ref={inputRef} type='text' placeholder='enter todo'/>
        <button onClick={onAdd}> add todo </button>
        <TodoList todos={todos}/>
      </React.Fragment>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

Anti PureComponenet

در مثال موجود، با توجه به این که todos (که مشابه به App است و به عنوان یک prop به TodoList منتقل شده است) همیشه ارجاع مشابه را دارد، TodoList هیچ وقت مجددا رندر نخواهد شد.

چگونه آن را به کار بیندازیم؟

class TodoList extends React.PureComponent {
  render() {
    const { todos } = this.props;
    return (<ul>
      {
        todos.map((it, index) => <li key={index}> {it} </li>)
      }
    </ul>)
  }
}

class App extends React.Component {
  inputRef = React.createRef()

  state = {
    todos: []
  }

  onAdd = () => {
    let { todos } = this.state;
    const text = this.inputRef.current.value;
    this.setState({
      todos: [...todos, text]
    });
  }

  render() {
    const { todos } = this.state;
    const { onAdd, inputRef } = this
    return (
      <React.Fragment>
        <input ref={inputRef} type='text' placeholder='enter todo'/>
        <button onClick={onAdd}> add todo </button>
        <TodoList todos={todos}/>
      </React.Fragment>
    )
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

ارجاع PureComponent

حال که هر زمان ما setState را فراخوانی می‌کنیم، یک آرایه todos جدید می‌سازیم، این کد به درستی کار می‌کند.

React.memo - برای کامپوننت تابعی

نحوه کار آن

let count = 0;

const Button = React.memo(function(props) {
  const { color } = props
  count++;
  return (
    <React.Fragment>
       <span> Render count: { count } </span>
       <button style={{ color }}> text </button>
      </React.Fragment>
    )
})

class App extends React.Component {
  state = {
    color: 'red'
  }

  showRedButton = () => {
    this.setState({
      color: 'red'
    })
  }

  showGreenButton = () => {
    this.setState({
      color: 'green'
    })
  }

  render() {
    const { color } = this.state;
    const { showRedButton, showGreenButton } = this;
    return (
      <React.Fragment>
        <Button color={color}/>
        <button onClick={showRedButton}> red </button>
        <button onClick={showGreenButton}> green </button>
     </React.Fragment>)
  }
}

ReactDOM.render(<App/>, document.getElementById('root'))

منبع

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید