مدیریت داده ری اکت: React state و lifecycle

همانطور که میدانید مدیریت داده ری اکت یا همان state در React کاربردی است. از Redux و Mobx هم استفاده میکنند. خود ورژن 16 ری اکت هم از context رونمایی کرد تا مدیریت داده ها در state بهتر انجام گیرد. برخی توسعه دهندگان معتقدند با وجود context دیگر نیازی به Redux نیست. در این مقاله پاره ای از  صفحه اصلی سایت ری اکت درباره state ترجمه میشود.

با مدیریت داده ری اکت مینوان یک ساعت ساخت که هرلحظه اپدیت میشود.

یک کمپوننت در دو شکل متفاوت

دو کمپوننت زیر را در نظر بگیرید که یکی کمپوننتی است که با تابع ساخته شده است. به ان  functional component میگویند. دیگری کمپوننتی است که با کلاس ES6 ساخته شده و constructor دارد. هر دو یک کار را در بروزر برای ما انجام میدهند اما ما برای اینکه با ری اکت کار کنیم باید دومی را بیاموزیم!

مثال اول: تعریف با تابع

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

مثال دوم: تعریف با کلاس ES6

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
با مدیریت داده ری اکت و لایف سایکل متد هایش مثل componentDidMount کارهای جالبی میتوان انجام داد.

مدیریت داده ری اکت: مسیر داده ها در state در کلاس ES6 – شرح مثال ۲

مرحله 1: وقتی </Clock> به ReactDOM.render فرستاده میشود، ری اکت constructor را فرامیخواند. از انجاییکه کمپوننت clock باید هر ثانیه اپدیت شود،  از طریق this.state اقدام میکند.  

مرحله 2: سپس ری اکت، متد render در کمپوننت clock را فراخوانی میکند. به این ترتیب ری اکت متوجه میشود چه چیزی باید در صفحه نمایش نشان بدهد. سپس ری اکت DOM را به روز رسانی میکند.

مرحله 3: خروجی render در کمپوننت clock در DOM نمایش داده شد. متد componentDidMount یک متد lifecycle بود. حالا این متد توسط ری اکت فراخوانی میشود. کمپوننت clock از بروزر درخواست میکند تایمر راه اندازی کند. تایمری که هر 1000 میلی ثانیه – معادل یک ثانیه – متد tick را فراخوانی کند. یادتان است که این تایمر با setInterval در داخل componentDidMount راه اندازی کردیم؟

مرحله 4: بروزر هر یک ثانیه یکبار متد tick را فراخوانی میکند. داخل این متد داده های state با متد setState به روز رسانی میشوند. یک ابجکت حاوی تایم کنونی است. ری اکت متوجه میشود داده های state تغییر کرده است.  بنابراین یکبار دیگر متد render را فراخوانی میکند. به این ترتیب اطلاعات به روز رسانی میشود.

مرحله 5: اگر حتی کمپوننت clock از DOM محو شود تا زمانی که تایمر متوقف شود، ری اکت متد componentWillUnmount را فراخوانی میکند .

روش صحیح مدیریت state

سه نکته مدیریت داده ری اکت درباره setState بدانید.

نکته 1: استیت را مستقیم ویرایش نکن! تنها جایی که از this.state استفاده میشود داخل constructor است.

// Correct
this.setState({comment: 'Hello'});

نکته 2: اپدیت استیت بصورت همزمان Async میتواند اتفاق بیفتد زیرا this.props و this.state ممکن است بطور همزمان به روز رسانی شوند.  اینجا از تابع بجای ابجکت استفاده میکنیم.

با ابجکت نادرست است

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

با این تابع ES6 درست است.

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

با این تابع ES5 درست است.

// Correct
this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

نکته 3: مدیریت داده ری اکت و به روز رسانی استیت بصورت merge میتواند اتفاق بیفتد. مثلا وقتی state شما یک ابجکت باشد که دو تا key داشته باشد مثل posts و comments در مثال زیر،

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

انگاه در متد لایف سایکل componentDidMount به ازای هریک تابعی نوشته میشود.  اینجا از then هم استفاده کردیم.

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

مسیر انتقال داده ها به بچه کمپوننت ها

چه کمپوننت مادر چه بچه کمپوننت ها میتوانند با داشتن props  به داده ها دسترسی داشته باشند! فرقی هم ندارد بچه با تابع تعریف شده باشد یا کلاس! یک کمپوننت مادر میتواند بصورت props داده های استیت خود را به فرزندان منتقل کند.

<FormattedDate date={this.state.date} />

سپس فرزند اگر با تابع تعریف شده باشد این props را بعنوان ورودی تابع دریافت میکند.

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}