From 7b40b74edda7a92fdcef597c82b935bea573c886 Mon Sep 17 00:00:00 2001 From: Soroush Torkzadeh Date: Wed, 6 Mar 2019 22:41:59 +0330 Subject: [PATCH 01/22] Translate content/docs/state-and-lifecycle.md --- content/docs/state-and-lifecycle.md | 173 ++++++++++++++-------------- 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/content/docs/state-and-lifecycle.md b/content/docs/state-and-lifecycle.md index cbd8f684a..389ba867d 100644 --- a/content/docs/state-and-lifecycle.md +++ b/content/docs/state-and-lifecycle.md @@ -1,6 +1,6 @@ --- id: state-and-lifecycle -title: State and Lifecycle +title: state و lifecycle permalink: docs/state-and-lifecycle.html redirect_from: - "docs/interactivity-and-dynamic-uis.html" @@ -8,9 +8,9 @@ prev: components-and-props.html next: handling-events.html --- -This page introduces the concept of state and lifecycle in a React component. You can find a [detailed component API reference here](/docs/react-component.html). +این صفحه مفهوم state و lifecycle را در یک کامپوننت ری‌اکتی معرفی می‌کند. برای مطالعه [مرجع API کامپوننت با جزئیات به اینجا](/docs/react-component.html) مراجعه کنید. -Consider the ticking clock example from [one of the previous sections](/docs/rendering-elements.html#updating-the-rendered-element). In [Rendering Elements](/docs/rendering-elements.html#rendering-an-element-into-the-dom), we have only learned one way to update the UI. We call `ReactDOM.render()` to change the rendered output: +مثال ساعت را در [یکی از بخش‌های پیشین](/docs/rendering-elements.html#updating-the-rendered-element) در نظر بگیرید. در [رندر کردن المنت‌ها](/docs/rendering-elements.html#rendering-an-element-into-the-dom)، ما تنها یک راه برای به‌روز رسانی UI یاد گرفتیم. ما تابع `ReactDOM.render()` را فراخوانی می‌کنیم تا خروجی رندر شده را تغییر دهیم: ```js{8-11} function tick() { @@ -29,11 +29,11 @@ function tick() { setInterval(tick, 1000); ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/gwoJZk?editors=0010) +[**روی ‌CodePen امتحان کنید**](https://codepen.io/gaearon/pen/gwoJZk?editors=0010) -In this section, we will learn how to make the `Clock` component truly reusable and encapsulated. It will set up its own timer and update itself every second. +در این بخش یاد می‌گیریم که چگونه کامپوننت `Clock` را واقعا قابل استفاده مجدد و کپسوله کنیم. به طوری که خودش تایمرش را تنظیم و هر ثانیه خودش را به‌روز رسانی کند. -We can start by encapsulating how the clock looks: +می تونیم با کپسوله کردن ظاهر ساعت شروع کنیم: ```js{3-6,12} function Clock(props) { @@ -55,11 +55,11 @@ function tick() { setInterval(tick, 1000); ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/dpdoYR?editors=0010) +[**روی ‌CodePen امتحان کنید**](https://codepen.io/gaearon/pen/dpdoYR?editors=0010) -However, it misses a crucial requirement: the fact that the `Clock` sets up a timer and updates the UI every second should be an implementation detail of the `Clock`. +اگرچه، یک نیاز حیاتی را از دست می‌دهد: این که `Clock` یک تایمر را تنظیم و هر ثانیه UI را به‌روز‌ رسانی کند، که باید بخشی از جزئیات پیاده‌سازی خود `Clock` باشد. -Ideally we want to write this once and have the `Clock` update itself: +ایده‌آل این است که ما یک بار `Clock` را بنویسیم و خودش به‌روز رسانی را انجام دهد: ```js{2} ReactDOM.render( @@ -68,25 +68,26 @@ ReactDOM.render( ); ``` -To implement this, we need to add "state" to the `Clock` component. +برای پیاده‌سازی آن، باید به کامپوننت `Clock` state اضافه کنیم. -State is similar to props, but it is private and fully controlled by the component. +state مشابه props است، اما خصوصی است و کاملا توسط کامپوننت کنترل می‌شود. -We [mentioned before](/docs/components-and-props.html#functional-and-class-components) that components defined as classes have some additional features. Local state is exactly that: a feature available only to classes. +ما [قبلا ذکر کردیم](/docs/components-and-props.html#functional-and-class-components) به کامپوننت‌هایی که به صورت کلاس تعریف می‌شوند، قابلیت‌هایی افزوده می‌شود. state داخلی دقیقا همان است: قابلیتی که فقط برای کلاس‌ها موجود است. -## Converting a Function to a Class {#converting-a-function-to-a-class} -You can convert a function component like `Clock` to a class in five steps: +## تبدیل یک تابع به یک کلاس {#converting-a-function-to-a-class} -1. Create an [ES6 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes), with the same name, that extends `React.Component`. +شما می‌توانید یک کامپوننت برپایه تابع را در پنج مرحله به یک کلاس تبدیل کنید: -2. Add a single empty method to it called `render()`. +1. یک [کلاس ES6](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) بسازید، با همان نام که از `React.Component` ارث می‌برد. -3. Move the body of the function into the `render()` method. +2. یک متود خالی با نام `render()` به آن اضافه کنید. -4. Replace `props` with `this.props` in the `render()` body. +3. بدنه تابع را به متود `render()` منتقل کنید. -5. Delete the remaining empty function declaration. +4. در بدنه `render()` به‌جای `props`، `this.props` بنویسید. + +5. آن‌چه از تعریف تابع خالی باقی مانده‌است را پاک کنید. ```js class Clock extends React.Component { @@ -101,17 +102,18 @@ class Clock extends React.Component { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/zKRGpo?editors=0010) +[**روی CodePen امتحان کنید**](https://codepen.io/gaearon/pen/zKRGpo?editors=0010) + +حالا `Clock` به جای تابع، به صورت کلاس تعریف شده‌است. -`Clock` is now defined as a class rather than a function. +هربار که به‌روز رسانی اتفاق می‌افتد، تابع `render` فراخوانی می‌شود. اما تا وقتی که ما `` را درون همان DOM node رندر می‌کنیم، تنها یک نمونه از کلاس `Clock` استفاده خواهدشد. این باعث می‌شود ما قادر به استفاده از ویژگی‌های دیگری مانند state و توابع lifecycle باشیم. -The `render` method will be called each time an update happens, but as long as we render `` into the same DOM node, only a single instance of the `Clock` class will be used. This lets us use additional features such as local state and lifecycle methods. -## Adding Local State to a Class {#adding-local-state-to-a-class} +## اضافه‌کردن state داخلی به یک کلاس {#adding-local-state-to-a-class} -We will move the `date` from props to state in three steps: +ما در سه مرحله، `date` را از props به state انتقال می‌دهیم: -1) Replace `this.props.date` with `this.state.date` in the `render()` method: +1. در تابع `render()`، `this.props.date` را با `this.state.date` جایگزین کنید: ```js{6} class Clock extends React.Component { @@ -126,7 +128,7 @@ class Clock extends React.Component { } ``` -2) Add a [class constructor](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes#Constructor) that assigns the initial `this.state`: +2. یک [سازنده کلاس](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes#Constructor) اضافه کنید که مقداردهی اولیه `this.state` را انجام می‌دهد: ```js{4} class Clock extends React.Component { @@ -146,7 +148,7 @@ class Clock extends React.Component { } ``` -Note how we pass `props` to the base constructor: +آگاه باشید که ما چگونه `props` را به سازنده پدر پاس می‌دهیم: ```js{2} constructor(props) { @@ -155,9 +157,9 @@ Note how we pass `props` to the base constructor: } ``` -Class components should always call the base constructor with `props`. +کامپوننت‌های بر پایه کلاس باید همیشه سازنده پدر را با `props` فراخوانی کنند. -3) Remove the `date` prop from the `` element: +3. `date` را از props المنت `` حذف کنید: ```js{2} ReactDOM.render( @@ -166,9 +168,9 @@ ReactDOM.render( ); ``` -We will later add the timer code back to the component itself. +ما بعدا کد مربوط به تایمر را به خود کامپوننت اضافه‌می کنیم. -The result looks like this: +نتیجه این شده‌است: ```js{2-5,11,18} class Clock extends React.Component { @@ -193,19 +195,19 @@ ReactDOM.render( ); ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/KgQpJd?editors=0010) +[**روی CodePen امتحان کنید**](https://codepen.io/gaearon/pen/KgQpJd?editors=0010) -Next, we'll make the `Clock` set up its own timer and update itself every second. +در ادامه، `Clock` را طوری تغییر می‌دهیم که تایمر خودش را داشته‌باشد و خودش هر ثانیه به‌روز رسانی شود. -## Adding Lifecycle Methods to a Class {#adding-lifecycle-methods-to-a-class} +## افزودن توابع lifecycle به یک کلاس {#adding-lifecycle-methods-to-a-class} -In applications with many components, it's very important to free up resources taken by the components when they are destroyed. +در برنامه‌هایی با تعداد کامپوننت زیاد، بسیار اهمیت دارد که با از بین رفتن کامپوننت، منابعی که توسط آن اشغال شده‌بود نیز آزاد شود. -We want to [set up a timer](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval) whenever the `Clock` is rendered to the DOM for the first time. This is called "mounting" in React. +ما می خواهیم [یک تایمر](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval) را برای اولین دفعه‌ای که `Clock` در DOM رندر می‌شود، تنظیم کنیم. در ری‌اکت به آن "mounting" گفته می‌شود. -We also want to [clear that timer](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval) whenever the DOM produced by the `Clock` is removed. This is called "unmounting" in React. +همچنین می‌خواهیم هر زمان که DOM تولید شده توسط `Clock` حذف می‌شود، [تایمر را پاک کنیم](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval). در ری‌اکت به آن "unmounting" گفته می‌شود. -We can declare special methods on the component class to run some code when a component mounts and unmounts: +برای اجرای کد‌هایی زمان mount و unmount شدن یک کامپوننت، ما می توانیم توابع ویژه‌ای روی کامپوننت‌های بر پایه کلاس تعریف کنیم: ```js{7-9,11-13} class Clock extends React.Component { @@ -233,9 +235,9 @@ class Clock extends React.Component { } ``` -These methods are called "lifecycle methods". +به آن‌ها "توابع lifecycle" گفته می‌شود. -The `componentDidMount()` method runs after the component output has been rendered to the DOM. This is a good place to set up a timer: +تابع `componentDidMount()` پس از رندر شدن خروجی کامپوننت توی DOM، اجرا می‌شود. اینجا محل خوبی برای تنظیم یک تایمر است: ```js{2-5} componentDidMount() { @@ -246,11 +248,13 @@ The `componentDidMount()` method runs after the component output has been render } ``` -Note how we save the timer ID right on `this`. -While `this.props` is set up by React itself and `this.state` has a special meaning, you are free to add additional fields to the class manually if you need to store something that doesn’t participate in the data flow (like a timer ID). -We will tear down the timer in the `componentWillUnmount()` lifecycle method: +دقت داشته‌باشید که چگونه ما شناسه تایمر را دقیقا روی `this` ذخیره می‌کنیم. + +در حالی که `this.props` توسط خود ری‌اکت تنظیم می‌شود و `this.state` کاربرد خاص خودش را دارد، شما آزاد هستید که برای ذخیره چیزی که نقشی در جریان داده‌ها ندارد (مانند شناسه تایمر)، به صورت دستی فیلد‌های دیگری به کلاس اضافه کنید. + +ما تایمر را در تابع lifecycle `componentWillUnmount()` از کار می‌اندازیم: ```js{2} componentWillUnmount() { @@ -258,9 +262,9 @@ We will tear down the timer in the `componentWillUnmount()` lifecycle method: } ``` -Finally, we will implement a method called `tick()` that the `Clock` component will run every second. +در آخر، ما تابعی با نام `tick()` پیاده‌سازی می‌کنیم که کامپوننت `Clock` هر ثانیه آن‌را فراخوانی می‌کند. -It will use `this.setState()` to schedule updates to the component local state: +این [تابع] از `this.setState()` استفاده می کند تا به‌روز رسانی‌ها را روی state داخلی کامپوننت انجام دهد: ```js{18-22} class Clock extends React.Component { @@ -302,51 +306,50 @@ ReactDOM.render( ); ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/amqdNA?editors=0010) +[**روی CodePen امتحان کنید**](https://codepen.io/gaearon/pen/amqdNA?editors=0010) -Now the clock ticks every second. +حالا ساعت هر ثانیه تغییر می‌کند. -Let's quickly recap what's going on and the order in which the methods are called: +بیایید به طور خلاصه جمع‌بندی کنیم که چه اتفاقی ره می‌دهد و به ترتیب چه توابعی فراخوانی می‌شوند: -1) When `` is passed to `ReactDOM.render()`, React calls the constructor of the `Clock` component. Since `Clock` needs to display the current time, it initializes `this.state` with an object including the current time. We will later update this state. +1. زمانی که `` به `ReactDOM.render()` پاس داده می‌شود، ری‌اکت سازنده کامپوننت `Clock` را فراخوانی می‌کند. از آن‌جایی که `Clock` زمان جاری را نمایش دهد، `this.state` را با شیء‌ای شامل زمان جاری مقداردهی اولیه می‌کند. -2) React then calls the `Clock` component's `render()` method. This is how React learns what should be displayed on the screen. React then updates the DOM to match the `Clock`'s render output. +2. سپس ری‌اکت تابع `render()` کامپوننت `Clock` را فرا می‌خواند. این روشی است که ری‌اکت می‌فهمد چه چیزی باید روی صفجه نمایش‌داده شود. سپس ری‌اکت DOM را به‌روز رسانی و با خروجی رندر `Clock` تطبیق می‌دهد. -3) When the `Clock` output is inserted in the DOM, React calls the `componentDidMount()` lifecycle method. Inside it, the `Clock` component asks the browser to set up a timer to call the component's `tick()` method once a second. +3. زمانی که خروجی `Clock` به DOM اضافه می‌شود، ری‌اکت تابع lifecycle `componentDidMount()` را فراخوانی می‌کند. درون آن، کامپوننت `Clock` از مرورگر می‌خواهد که یک تایمر تنظیم کند که هر ثانیه تابع `tick()` را فراخوانی کند. -4) Every second the browser calls the `tick()` method. Inside it, the `Clock` component schedules a UI update by calling `setState()` with an object containing the current time. Thanks to the `setState()` call, React knows the state has changed, and calls the `render()` method again to learn what should be on the screen. This time, `this.state.date` in the `render()` method will be different, and so the render output will include the updated time. React updates the DOM accordingly. +4. مرورگر هر ثانیه تابع `tick()` را فرا می‌خواند. درون آن، کامپوننت `Clock` به‌روز رسانی UI را با فراخوانی تابع `setState()` همراه یک شیء شامل زمان جاری، زمان‌بندی می‌کند. ری‌اکت با کمک `setState()` متوجه می‌شود که state تغییر کرده‌است. و آن‌گاه برای اطلاع از آن‌چه باید روی صفحه نمایش‌ داده‌شود، تابع `render()` را فرا می‌خواند. این دفعه، مقدار `this.state.date` در تابع `render()` متفاوت خواهد بود و بنابراین خروجی رندر دارای زمان به‌روز رسانی شده خواهدبود. ری‌اکت DOM را بر همین اساس به‌روز‌ رسانی می‌کند. -5) If the `Clock` component is ever removed from the DOM, React calls the `componentWillUnmount()` lifecycle method so the timer is stopped. +5. اگر کامپوننت `Clock` از DOM حذف شود، ری‌اکت هم تابع lifecycle `componentWillUnmount()` را فرا می‌خواند و در نتیجه تایمر متوقف می‌شود. -## Using State Correctly {#using-state-correctly} +## استفاده صحیح از state {#using-state-correctly} -There are three things you should know about `setState()`. +سه چیز هست که باید درباره `setState()` بدانید: -### Do Not Modify State Directly {#do-not-modify-state-directly} +### مستقیم state را تغییر ندهید {#do-not-modify-state-directly} -For example, this will not re-render a component: +برای مثال، این کار باعث رندر مجدد یک کامپوننت نمی‌شود: ```js // Wrong this.state.comment = 'Hello'; ``` -Instead, use `setState()`: +به جای آن از `setState()` استفاده کنید: ```js // Correct this.setState({comment: 'Hello'}); ``` +تنها جایی که شما می‌توانید `this.state` را [مستقیم] مقدار‌دهی کنید، سازنده [کلاس] است. -The only place where you can assign `this.state` is the constructor. +### به‌روز رسانی state ممکن است ناهمزمان باشد {#state-updates-may-be-asynchronous} -### State Updates May Be Asynchronous {#state-updates-may-be-asynchronous} +ممکن است ری‌اکت برای بهبود عملکرد، فراخوانی چند باره `setState()` را در یک به‌روز رسانی انجام دهد. -React may batch multiple `setState()` calls into a single update for performance. +از آن‌جا که ممکن است `this.props` و `this.state` به صورت ناهمزمان به‌روز ‌رسانی شوند، برای محاسبه وضعیت بعدی نباید روی مقادیر آن‌ها حساب کنید. -Because `this.props` and `this.state` may be updated asynchronously, you should not rely on their values for calculating the next state. - -For example, this code may fail to update the counter: +برای مثال، این کد ممکن است در به‌روز رسانی شمارنده دچار اشکال شود: ```js // Wrong @@ -355,7 +358,7 @@ this.setState({ }); ``` -To fix it, use a second form of `setState()` that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument: +برای حل آن، شکل دوم تابع `setState()` را استفاده کنید که به جای یک شیء، یک تابع به عنوان ورودی می‌پذیرد. آن تابع هم state قبلی را به عنوان ورودی اول، و props مربوط به زمانی که تعییرات اعمال شده‌است را به عنوان ورودی دوم دریافت می‌کند. ```js // Correct @@ -364,7 +367,7 @@ this.setState((state, props) => ({ })); ``` -We used an [arrow function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) above, but it also works with regular functions: +در بالا ما از [تابع arrow](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) استفاده کرده‌ایم، اما با تابع معمولی هم کار می‌کند: ```js // Correct @@ -375,11 +378,11 @@ this.setState(function(state, props) { }); ``` -### State Updates are Merged {#state-updates-are-merged} +### به‌روز رسانی‌های state ادغام‌شده هستند {#state-updates-are-merged} -When you call `setState()`, React merges the object you provide into the current state. +وقتی شما `setState()` را فرا می‌خوانید، ری‌اکت آن شیء را در state فعلی ادغام می‌کند. -For example, your state may contain several independent variables: +برای مثال، state شما ممکن است دارای چندین متغییر مستقل باشد: ```js{4,5} constructor(props) { @@ -391,7 +394,7 @@ For example, your state may contain several independent variables: } ``` -Then you can update them independently with separate `setState()` calls: +پس شما می‌توانید آن‌ها را جدا از هم، با فراخوانی‌های مجزای `setState()`، به‌روز رسانی کنید: ```js{4,10} componentDidMount() { @@ -408,28 +411,26 @@ Then you can update them independently with separate `setState()` calls: }); } ``` +این ادغام سطحی است، بنابراین `this.setState({comments})` تاثیری روی `this.state.posts` ندارد، اما کاملا `this.state.comments` را جایگزین می‌کند. -The merging is shallow, so `this.setState({comments})` leaves `this.state.posts` intact, but completely replaces `this.state.comments`. - -## The Data Flows Down {#the-data-flows-down} +## داده به پایین جریان دارد {#the-data-flows-down} -Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn't care whether it is defined as a function or a class. +نه کامپوننت پدر و نه کامپوننت فرزند از این‌که یک کامپوننت مشخص دارای state است یا نه خبری ندارند و نباید برای آن‌ها مهم باشد که به صورت تابع یا کلاس تعریف شده‌است. -This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it. +به همین دلیل است که اغلب state را داخلی یا کپسوله‌شده خطاب می‌کنند. به غیر از کامپوننتی که مالک آن است و با آن کار می کند، توسط هیچ کامپوننت دیگری قابل دسترسی نیست. -A component may choose to pass its state down as props to its child components: +یک کامپوننت ممکن است خودش انتخاب کند که state خود را به عنوان props به کامپوننت‌های فرزند انتقال دهد. ```js

It is {this.state.date.toLocaleTimeString()}.

``` - -This also works for user-defined components: +همچنین برای کامپوننت‌هایی که توسط کاربر تعریف شده‌است، کار می کند. ```js ``` -The `FormattedDate` component would receive the `date` in its props and wouldn't know whether it came from the `Clock`'s state, from the `Clock`'s props, or was typed by hand: +کامپوننت `FormattedDate` از props خود `date` را دریافت می‌کند و نخواهد فهمید که از state یا props `Clock` به دریافت کرده، یا با دست تایپ شده‌است. ```js function FormattedDate(props) { @@ -437,13 +438,13 @@ function FormattedDate(props) { } ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/zKRqNB?editors=0010) +[**روی CodePen امتحان کنید**](https://codepen.io/gaearon/pen/zKRqNB?editors=0010) -This is commonly called a "top-down" or "unidirectional" data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components "below" them in the tree. +به این جریان داده عموما "یک طرفه" یا "بالا به پایین" گفته می‌شود. مالکیت هر state در دست یک کامپوننت مشخص است و هر داده یا UI که از آن state مشتق شده‌باشد، فقط کامپوننت‌های زیرین خود را در ساختار درختی تحت تاثیر قرار می‌دهد. -If you imagine a component tree as a waterfall of props, each component's state is like an additional water source that joins it at an arbitrary point but also flows down. +اگر یک درخت کامپوننت را به شکل آبشاری از props ها تصور کنید، state هر کامپوننت مانند منبعی اضافه از آب است که در نقطه‌ای دلخواه به آن متصل و به پایین جریان پیدا می‌کند. -To show that all components are truly isolated, we can create an `App` component that renders three ``s: +برای نشان‌دادن این‌که تمام کامپوننت‌ها واقعا ایزوله هستند، ما می‌توانیم یک کامپوننت `App` بسازیم که سه کامپوننت `` را رندر می‌کند. ```js{4-6} function App() { @@ -462,8 +463,8 @@ ReactDOM.render( ); ``` -[**Try it on CodePen**](https://codepen.io/gaearon/pen/vXdGmd?editors=0010) +[**روی CodePen امتحان کنید**](https://codepen.io/gaearon/pen/vXdGmd?editors=0010) -Each `Clock` sets up its own timer and updates independently. +هر `Clock` تایمر خودش را تنظیم و به طور مستقل به‌روز رسانی می‌کند. -In React apps, whether a component is stateful or stateless is considered an implementation detail of the component that may change over time. You can use stateless components inside stateful components, and vice versa. +در برنامه‌های ری‌اکتی، این‌که یک کامپوننت دارای state هست یا نه، به جزئیات طراحی آن کامپوننت مربوط می‌شود که ممکن است در طول زمان تغییر کند. شما می‌توانید کامپوننت‌های بدون state را درون کامپوننت های دارای state استفاده کنید و همچنین برعکس. From e9274f7c7424a4a416f8644f92e386edf688327a Mon Sep 17 00:00:00 2001 From: Soroush Torkzadeh Date: Thu, 7 Mar 2019 11:43:58 +0330 Subject: [PATCH 02/22] Apply some fixes in page "State and Lifecycle" page The changes have been requested by @zahrajoulaei --- content/docs/state-and-lifecycle.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/content/docs/state-and-lifecycle.md b/content/docs/state-and-lifecycle.md index 389ba867d..e6d85ae57 100644 --- a/content/docs/state-and-lifecycle.md +++ b/content/docs/state-and-lifecycle.md @@ -81,9 +81,9 @@ state مشابه props است، اما خصوصی است و کاملا توسط 1. یک [کلاس ES6](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) بسازید، با همان نام که از `React.Component` ارث می‌برد. -2. یک متود خالی با نام `render()` به آن اضافه کنید. +2. یک متد خالی با نام `render()` به آن اضافه کنید. -3. بدنه تابع را به متود `render()` منتقل کنید. +3. بدنه تابع را به متد `render()` منتقل کنید. 4. در بدنه `render()` به‌جای `props`، `this.props` بنویسید. @@ -331,28 +331,28 @@ ReactDOM.render( برای مثال، این کار باعث رندر مجدد یک کامپوننت نمی‌شود: ```js -// Wrong +// اشتباه this.state.comment = 'Hello'; ``` به جای آن از `setState()` استفاده کنید: ```js -// Correct +// درست this.setState({comment: 'Hello'}); ``` تنها جایی که شما می‌توانید `this.state` را [مستقیم] مقدار‌دهی کنید، سازنده [کلاس] است. -### به‌روز رسانی state ممکن است ناهمزمان باشد {#state-updates-may-be-asynchronous} +### به‌روز رسانی state ممکن است غیرهمزمان باشد {#state-updates-may-be-asynchronous} ممکن است ری‌اکت برای بهبود عملکرد، فراخوانی چند باره `setState()` را در یک به‌روز رسانی انجام دهد. -از آن‌جا که ممکن است `this.props` و `this.state` به صورت ناهمزمان به‌روز ‌رسانی شوند، برای محاسبه وضعیت بعدی نباید روی مقادیر آن‌ها حساب کنید. +از آن‌جا که ممکن است `this.props` و `this.state` به صورت غیرهمزمان به‌روز ‌رسانی شوند، برای محاسبه وضعیت بعدی نباید روی مقادیر آن‌ها حساب کنید. برای مثال، این کد ممکن است در به‌روز رسانی شمارنده دچار اشکال شود: ```js -// Wrong +// اشتباه this.setState({ counter: this.state.counter + this.props.increment, }); @@ -361,7 +361,7 @@ this.setState({ برای حل آن، شکل دوم تابع `setState()` را استفاده کنید که به جای یک شیء، یک تابع به عنوان ورودی می‌پذیرد. آن تابع هم state قبلی را به عنوان ورودی اول، و props مربوط به زمانی که تعییرات اعمال شده‌است را به عنوان ورودی دوم دریافت می‌کند. ```js -// Correct +// درست this.setState((state, props) => ({ counter: state.counter + props.increment })); @@ -370,7 +370,7 @@ this.setState((state, props) => ({ در بالا ما از [تابع arrow](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions) استفاده کرده‌ایم، اما با تابع معمولی هم کار می‌کند: ```js -// Correct +// درست this.setState(function(state, props) { return { counter: state.counter + props.increment From acb2fd43308c117fb283ea04a8e00bcc0bc01431 Mon Sep 17 00:00:00 2001 From: lsdsjy Date: Sat, 9 Mar 2019 16:21:51 +0800 Subject: [PATCH 03/22] Update render-props.md (#1776) Update React Router render func URL --- content/docs/render-props.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/render-props.md b/content/docs/render-props.md index c9b6f9c04..e1482f491 100644 --- a/content/docs/render-props.md +++ b/content/docs/render-props.md @@ -14,7 +14,7 @@ A component with a render prop takes a function that returns a React element and )}/> ``` -Libraries that use render props include [React Router](https://reacttraining.com/react-router/web/api/Route/Route-render-methods) and [Downshift](https://github.com/paypal/downshift). +Libraries that use render props include [React Router](https://reacttraining.com/react-router/web/api/Route/render-func) and [Downshift](https://github.com/paypal/downshift). In this document, we’ll discuss why render props are useful, and how to write your own. From d6d30b6c352c315577f240d7e18486cbe9690e48 Mon Sep 17 00:00:00 2001 From: Mark Murray Date: Sat, 9 Mar 2019 08:27:44 +0000 Subject: [PATCH 04/22] Rename App class component to Counter (#1739) --- content/docs/addons-test-utils.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/addons-test-utils.md b/content/docs/addons-test-utils.md index a6e06e1be..6cf2af081 100644 --- a/content/docs/addons-test-utils.md +++ b/content/docs/addons-test-utils.md @@ -53,7 +53,7 @@ To prepare a component for assertions, wrap the code rendering it and performing For example, let's say we have this `Counter` component: ```js -class App extends React.Component { +class Counter extends React.Component { constructor(props) { super(props); this.state = {count: 0}; From c0f4b34282a2ce8391121c8cf274a4b6bd7d7c90 Mon Sep 17 00:00:00 2001 From: Luan Ferreira Date: Mon, 11 Mar 2019 00:02:34 -0300 Subject: [PATCH 05/22] Fix small typo in blog post (#1803) --- content/blog/2019-02-23-is-react-translated-yet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2019-02-23-is-react-translated-yet.md b/content/blog/2019-02-23-is-react-translated-yet.md index 78bc49b6e..3ac56a5a4 100644 --- a/content/blog/2019-02-23-is-react-translated-yet.md +++ b/content/blog/2019-02-23-is-react-translated-yet.md @@ -43,7 +43,7 @@ We started of with an initial trial period of three languages: Spanish, Japanese After the trial period, we were ready to accept more languages. I created [a script](https://github.com/reactjs/reactjs.org-translation/blob/master/scripts/create.js) to automate the creation of the new language repo, and a site, [Is React Translated Yet?](https://isreacttranslatedyet.com), to track progress on the different translations. We started *10* new translations on our first day alone! -Because of the automation, the rest of the maintance went mostly smoothly. We eventually created a [Slack channel](https://rt-slack-invite.herokuapp.com) to make it easier for translators to share information, and I released a guide solidifying the [responsibilities of maintainers](https://github.com/reactjs/reactjs.org-translation/blob/master/maintainer-guide.md). Allowing translators to talk with each other was a great boon -- for example, the Arabic, Persian, and Hebrew translations were able to talk to each other in order to get [right-to-left text](https://en.wikipedia.org/wiki/Right-to-left) working! +Because of the automation, the rest of the maintenance went mostly smoothly. We eventually created a [Slack channel](https://rt-slack-invite.herokuapp.com) to make it easier for translators to share information, and I released a guide solidifying the [responsibilities of maintainers](https://github.com/reactjs/reactjs.org-translation/blob/master/maintainer-guide.md). Allowing translators to talk with each other was a great boon -- for example, the Arabic, Persian, and Hebrew translations were able to talk to each other in order to get [right-to-left text](https://en.wikipedia.org/wiki/Right-to-left) working! ## The Bot {#the-bot} From 439584a6f4070f34a92dd62ae1217d4009d8bcdd Mon Sep 17 00:00:00 2001 From: Nat Alison Date: Mon, 11 Mar 2019 12:48:20 -0700 Subject: [PATCH 06/22] Update languages.yml (#1808) * Brazilian Portuguese is complete! * Update status of Hebrew, Polish, German --- content/languages.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/languages.yml b/content/languages.yml index ff0be2c1d..d34224dda 100644 --- a/content/languages.yml +++ b/content/languages.yml @@ -26,7 +26,7 @@ - name: German translated_name: Deutsch code: de - status: 0 + status: 1 - name: Greek translated_name: Ελληνικά code: el @@ -50,7 +50,7 @@ - name: Hebrew translated_name: עברית code: he - status: 0 + status: 1 - name: Hindi translated_name: हिन्दी code: hi @@ -102,11 +102,11 @@ - name: Polish translated_name: Polski code: pl - status: 0 + status: 1 - name: Portuguese (Brazil) translated_name: Português do Brasil code: pt-br - status: 1 + status: 2 - name: Portuguese (Portugal) translated_name: Português europeu code: pt-pt From d827d5958a7d01b278b5e6e3428d6d6676ae7dd0 Mon Sep 17 00:00:00 2001 From: Nat Alison Date: Wed, 13 Mar 2019 03:34:06 -0700 Subject: [PATCH 07/22] Update languages.yml (#1820) * Russian is complete! * Move some other languages up --- content/languages.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/languages.yml b/content/languages.yml index d34224dda..5e0958260 100644 --- a/content/languages.yml +++ b/content/languages.yml @@ -78,7 +78,7 @@ - name: Korean translated_name: 한국어 code: ko - status: 0 + status: 1 - name: Kurdish translated_name: کوردی‎ code: ku @@ -118,7 +118,7 @@ - name: Russian translated_name: Русский code: ru - status: 1 + status: 2 - name: Sinhala translated_name: සිංහල code: si @@ -137,7 +137,7 @@ - name: Turkish translated_name: Türkçe code: tr - status: 0 + status: 1 - name: Ukrainian translated_name: Українська code: uk From 2cd4d0cf5ddadf90446b3a5038a9bc4875151355 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 13:59:42 +0000 Subject: [PATCH 08/22] Add more info to FAQ about deps (#1815) * Add more info to FAQ about deps * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-effect.md Co-Authored-By: gaearon * tweaks * tweak --- content/docs/hooks-custom.md | 24 ++-- content/docs/hooks-effect.md | 37 +++-- content/docs/hooks-faq.md | 248 +++++++++++++++++++++++++++++++- content/docs/hooks-reference.md | 41 ++++-- content/docs/hooks-rules.md | 3 +- 5 files changed, 316 insertions(+), 37 deletions(-) diff --git a/content/docs/hooks-custom.md b/content/docs/hooks-custom.md index ac9dad6da..a252ac3f3 100644 --- a/content/docs/hooks-custom.md +++ b/content/docs/hooks-custom.md @@ -18,11 +18,11 @@ import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -44,11 +44,11 @@ import React, { useState, useEffect } from 'react'; function FriendListItem(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -79,11 +79,11 @@ import React, { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 64b32476e..fd7d89592 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -198,17 +198,17 @@ Let's see how we could write this component with Hooks. You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that `useEffect` is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up: -```js{10-16} +```js{6-16} import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } - useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Specify how to clean up after this effect: return function cleanup() { @@ -237,6 +237,10 @@ We've learned that `useEffect` lets us express different kinds of side effects a ```js useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -316,15 +320,15 @@ function FriendStatusWithCounter(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); - - function handleStatusChange(status) { - setIsOnline(status.isOnline); - } // ... } ``` @@ -394,6 +398,7 @@ Now consider the version of this component that uses Hooks: function FriendStatus(props) { // ... useEffect(() => { + // ... ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -449,8 +454,12 @@ When we render with `count` updated to `6`, React will compare the items in the This also works for effects that have a cleanup phase: -```js{6} +```js{10} useEffect(() => { + function handleStatusChange(status) { + setIsOnline(status.isOnline); + } + ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); @@ -462,9 +471,13 @@ In the future, the second argument might get added automatically by a build-time >Note > ->If you use this optimization, make sure the array includes **any values from the outer scope that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. We'll also discuss other optimization options in the [Hooks API reference](/docs/hooks-reference.html). +>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and [what to do when the array changes too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often). +> +>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. +> +>If you pass an empty array (`[]`), the props and state inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. > ->If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. While passing `[]` is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, we suggest not making it a habit because it often leads to bugs, [as discussed above](#explanation-why-effects-run-on-each-update). Don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ## Next Steps {#next-steps} diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index b3b99de90..6145b968d 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -32,16 +32,20 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo * [What exactly do the lint rules enforce?](#what-exactly-do-the-lint-rules-enforce) * **[From Classes to Hooks](#from-classes-to-hooks)** * [How do lifecycle methods correspond to Hooks?](#how-do-lifecycle-methods-correspond-to-hooks) + * [How can I do data fetching with Hooks?](#how-can-i-do-data-fetching-with-hooks) * [Is there something like instance variables?](#is-there-something-like-instance-variables) * [Should I use one or many state variables?](#should-i-use-one-or-many-state-variables) * [Can I run an effect only on updates?](#can-i-run-an-effect-only-on-updates) * [How to get the previous props or state?](#how-to-get-the-previous-props-or-state) + * [Why am I seeing stale props or state inside my function?](#why-am-i-seeing-stale-props-or-state-inside-my-function) * [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops) * [Is there something like forceUpdate?](#is-there-something-like-forceupdate) * [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component) * [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean) * **[Performance Optimizations](#performance-optimizations)** * [Can I skip an effect on updates?](#can-i-skip-an-effect-on-updates) + * [Is it safe to omit functions from the list of dependencies?](#is-it-safe-to-omit-functions-from-the-list-of-dependencies) + * [What can I do if my effect dependencies change too often?](#what-can-i-do-if-my-effect-dependencies-change-too-often) * [How do I implement shouldComponentUpdate?](#how-do-i-implement-shouldcomponentupdate) * [How to memoize calculations?](#how-to-memoize-calculations) * [How to create expensive objects lazily?](#how-to-create-expensive-objects-lazily) @@ -204,6 +208,10 @@ There are a few more heuristics, and they might change over time as we fine-tune * `componentDidCatch` and `getDerivedStateFromError`: There are no Hook equivalents for these methods yet, but they will be added soon. +### How can I do data fetching with Hooks? + +Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. + ### Is there something like instance variables? {#is-there-something-like-instance-variables} Yes! The [`useRef()`](/docs/hooks-reference.html#useref) Hook isn't just for DOM refs. The "ref" object is a generic container whose `current` property is mutable and can hold any value, similar to an instance property on a class. @@ -362,6 +370,44 @@ It's possible that in the future React will provide a `usePrevious` Hook out of See also [the recommended pattern for derived state](#how-do-i-implement-getderivedstatefromprops). +### Why am I seeing stale props or state inside my function? {#why-am-i-seeing-stale-props-or-state-inside-my-function} + +Any function inside a component, including event handlers and effects, "sees" the props and state from the render it was created in. For example, consider code like this: + +```js +function Example() { + const [count, setCount] = useState(0); + + function handleAlertClick() { + setTimeout(() => { + alert('You clicked on: ' + count); + }, 3000); + } + + return ( +
+

You clicked {count} times

+ + +
+ ); +} +``` + +If you first click "Show alert" and then increment the counter, the alert will show the `count` variable **at the time you clicked the "Show alert" button**. This prevents bugs caused by the code assuming props and state don't change. + +If you intentionally want to read the *latest* state from some asynchronous callback, you could keep it in [a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables), mutate it, and read from it. + +Finally, another possible reason you're seeing stale props or state is if you use the "dependency array" optimization but didn't correctly specify all the dependencies. For example, if an effect specifies `[]` as the second argument but reads `someProp` inside, it will keep "seeing" the initial value of `someProp`. The solution is to either remove the dependency array, or to fix it. Here's [how you can deal with functions](#is-it-safe-to-omit-functions-from-the-list-of-dependencies), and here's [other common strategies](#what-can-i-do-if-my-effect-dependencies-change-too-often) to run effects less often without incorrectly skipping dependencies. + +>Note +> +>We provide an [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. + ### How do I implement `getDerivedStateFromProps`? {#how-do-i-implement-getderivedstatefromprops} While you probably [don't need it](/blog/2018/06/07/you-probably-dont-need-derived-state.html), in rare cases that you do (such as implementing a `` component), you can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render so it wouldn't be expensive. @@ -416,6 +462,207 @@ If you're not familiar with this syntax, check out the [explanation](/docs/hooks Yes. See [conditionally firing an effect](/docs/hooks-reference.html#conditionally-firing-an-effect). Note that forgetting to handle updates often [introduces bugs](/docs/hooks-effect.html#explanation-why-effects-run-on-each-update), which is why this isn't the default behavior. +### Is it safe to omit functions from the list of dependencies? {#is-it-safe-to-omit-functions-from-the-list-of-dependencies} + +Generally speaking, no. + +```js{3,8} +function Example() { + function doSomething() { + console.log(someProp); + } + + useEffect(() => { + doSomething(); + }, []); // 🔴 This is not safe (it calls `doSomething` which uses `someProp`) +} +``` + +It's difficult to remember which props or state are used by functions outside of the effect. This is why **usually you'll want to declare functions needed by an effect *inside* of it.** Then it's easy to see what values from the component scope that effect depends on: + +```js{4,8} +function Example() { + useEffect(() => { + function doSomething() { + console.log(someProp); + } + + doSomething(); + }, [someProp]); // ✅ OK (our effect only uses `someProp`) +} +``` + +If after that we still don't use any values from the component scope, it's safe to specify `[]`: + +```js{7} +useEffect(() => { + function doSomething() { + console.log('hello'); + } + + doSomething(); +}, []); // ✅ OK in this example because we don't use *any* values from component scope +``` + +Depending on your use case, there are a few more options described below. + +>Note +> +>We provide the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) ESLint rule as a part of the [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It help you find components that don't handle updates consistently. + +Let's see why this matters. + +If you specify a [list of dependencies](/docs/hooks-reference.html#conditionally-firing-an-effect) as the last argument to `useEffect`, `useMemo`, `useCallback`, or `useImperativeHandle`, it must include all values used inside that participate in the React data flow. That includes props, state, and anything derived from them. + +It is **only** safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them. This example has a bug: + +```js{5,12} +function ProductPage({ productId }) { + const [product, setProduct] = useState(null); + + async function fetchProduct() { + const response = await fetch('http://myapi/product' + productId); // Uses productId prop + const json = await response.json(); + setProduct(json); + } + + useEffect(() => { + fetchProduct(); + }, []); // 🔴 Invalid because `fetchProduct` uses `productId` + // ... +} +``` + +**The recommended fix is to move that function _inside_ of your effect**. That makes it easy to see which props or state your effect uses, and to ensure they're all declared: + +```js{5-10,13} +function ProductPage({ productId }) { + const [product, setProduct] = useState(null); + + useEffect(() => { + // By moving this function inside the effect, we can clearly see the values it uses. + async function fetchProduct() { + const response = await fetch('http://myapi/product' + productId); + const json = await response.json(); + setProduct(json); + } + + fetchProduct(); + }, [productId]); // ✅ Valid because our effect only uses productId + // ... +} +``` + +This also allows you to handle out-of-order responses with a local variable inside the effect: + +```js{2,6,8} + useEffect(() => { + let ignore = false; + async function fetchProduct() { + const response = await fetch('http://myapi/product/' + productId); + const json = await response.json(); + if (!ignore) setProduct(json); + } + return () => { ignore = true }; + }, [productId]); +``` + +We moved the function inside the effect so it doesn't need to be in its dependency list. + +>Tip +> +>Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. + +**If for some reason you _can't_ move a function inside an effect, there are a few more options:** + +* **You can try moving that function outside of your component**. In that case, the function is guaranteed to not reference any props or state, and also doesn't need to be in the list of dependencies. +* If the function you're calling is a pure computation and is safe to call while rendering, you may **call it outside of the effect instead,** and make the effect depend on the returned value. +* As a last resort, you can **add a function to effect dependencies but _wrap its definition_** into the [`useCallback`](/docs/hooks-reference.html#usecallback) Hook. This ensures it doesn't change on every render unless *its own* dependencies also change: + +```js{2-5} +function ProductPage({ productId }) { + // ✅ Wrap with useCallback to avoid change on every render + const fetchProduct = useCallback(() => { + // ... Does something with productId ... + }, [productId]); // ✅ All useCallback dependencies are specified + + return ; +} + +function ProductDetails({ fetchProduct }) + useEffect(() => { + fetchProduct(); + }, [fetchProduct]); // ✅ All useEffect dependencies are specified + // ... +} +``` + +Note that in the above example we **need** to keep the function in the dependencies list. This ensures that a change in the `productId` prop of `ProductPage` automatically triggers a refetch in the `ProductDetails` component. + +### What can I do if my effect dependencies change too often? + +Sometimes, your effect may be using reading state that changes too often. You might be tempted to omit that state from a list of dependencies, but that usually leads to bugs: + +```js{6,9} +function Counter() { + const [count, setCount] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setCount(count + 1); // This effect depends on the `count` state + }, 1000); + return () => clearInterval(id); + }, []); // 🔴 Bug: `count` is not specified as a dependency + + return

{count}

; +} +``` + +Specifying `[count]` as a list of dependencies would fix the bug, but would cause the interval to be reset on every change. That may not be desirable. To fix this, we can use the [functional update form of `setState`](/docs/hooks-reference.html#functional-updates). It lets us specify *how* the state needs to change without referencing the *current* state: + +```js{6,9} +function Counter() { + const [count, setCount] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside + }, 1000); + return () => clearInterval(id); + }, []); // ✅ Our effect doesn't use any variables in the component scope + + return

{count}

; +} +``` + +(The identity of the `setCount` function is guaranteed to be stable so it's safe to omit.) + +In more complex cases (such as if one state depends on another state), try moving the state update logic outside the effect with the [`useReducer` Hook](/docs/hooks-reference.html#usereducer). [This article](https://adamrackis.dev/state-and-use-reducer/) offers an example of how you can do this. **The identity of the `dispatch` function from `useReducer` is always stable** — even if the reducer function is declared inside the component and reads its props. + +As a last resort, if you want to something like `this` in a class, you can [use a ref](/docs/hooks-faq.html#is-there-something-like-instance-variables) to hold a mutable variable. Then you can write and read to it. For example: + +```js{2-6,10-11,16} +function Example(props) { + // Keep latest props in a ref. + let latestProps = useRef(props); + useEffect(() => { + latestProps.current = props; + }); + + useEffect(() => { + function tick() { + // Read latest props at any time + console.log(latestProps.current); + } + + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, []); // This effect never re-runs +} +``` + +Only do this if you couldn't find a better alternative, as relying on mutation makes components less predictable. If there's a specific pattern that doesn't translate well, [file an issue](https://github.com/facebook/react/issues/new) with a runnable example code and we can try to help. + ### How do I implement `shouldComponentUpdate`? {#how-do-i-implement-shouldcomponentupdate} You can wrap a function component with `React.memo` to shallowly compare its props: @@ -430,7 +677,6 @@ It's not a Hook because it doesn't compose like Hooks do. `React.memo` is equiva `React.memo` doesn't compare state because there is no single state object to compare. But you can make children pure too, or even [optimize individual children with `useMemo`](/docs/hooks-faq.html#how-to-memoize-calculations). - ### How to memoize calculations? {#how-to-memoize-calculations} The [`useMemo`](/docs/hooks-reference.html#usememo) Hook lets you cache calculations between multiple renders by "remembering" the previous computation: diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index deeef54cf..4466bc83b 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -45,6 +45,10 @@ setState(newState); During subsequent re-renders, the first value returned by `useState` will always be the most recent state after applying updates. +>Note +> +>React guarantees that `setState` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. + #### Functional updates {#functional-updates} If the new state is computed using the previous state, you can pass a function to `setState`. The function will receive the previous value, and return an updated value. Here's an example of a counter component that uses both forms of `setState`: @@ -133,7 +137,7 @@ Although `useEffect` is deferred until after the browser has painted, it's guara #### Conditionally firing an effect {#conditionally-firing-an-effect} -The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its inputs changes. +The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes. However, this may be overkill in some cases, like the subscription example from the previous section. We don't need to create a new subscription on every update, only if the `source` props has changed. @@ -153,11 +157,18 @@ useEffect( Now the subscription will only be recreated when `props.source` changes. -Passing in an empty array `[]` of inputs tells React that your effect doesn't depend on any values from the component, so that effect would run only on mount and clean up on unmount; it won't run on updates. - -> Note +>Note +> +>If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and what to do when the [array values change too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often). +> +>If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the dependencies array always works. > -> The array of inputs is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +>If you pass an empty array (`[]`), the props and state as inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +> +> +>We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. + +The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. ### `useContext` {#usecontext} @@ -211,6 +222,10 @@ function Counter({initialState}) { } ``` +>Note +> +>React guarantees that `dispatch` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. + #### Specifying the initial state {#specifying-the-initial-state} There’s two different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way to pass the initial state as a second argument: @@ -283,13 +298,15 @@ const memoizedCallback = useCallback( Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) callback. -Pass an inline callback and an array of inputs. `useCallback` will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`). +Pass an inline callback and an array of dependencies. `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`). -`useCallback(fn, inputs)` is equivalent to `useMemo(() => fn, inputs)`. +`useCallback(fn, deps)` is equivalent to `useMemo(() => fn, deps)`. > Note > -> The array of inputs is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +> The array of dependencies is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> +> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ### `useMemo` {#usememo} @@ -299,7 +316,7 @@ const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) value. -Pass a "create" function and an array of inputs. `useMemo` will only recompute the memoized value when one of the inputs has changed. This optimization helps to avoid expensive calculations on every render. +Pass a "create" function and an array of dependencies. `useMemo` will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render. Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`. @@ -309,7 +326,9 @@ If no array is provided, a new value will be computed whenever a new function in > Note > -> The array of inputs is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the inputs array. In the future, a sufficiently advanced compiler could create this array automatically. +> The array of dependencies is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> +> We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. ### `useRef` {#useref} @@ -342,7 +361,7 @@ Note that `useRef()` is useful for more than the `ref` attribute. It's [handy fo ### `useImperativeHandle` {#useimperativehandle} ```js -useImperativeHandle(ref, createHandle, [inputs]) +useImperativeHandle(ref, createHandle, [deps]) ``` `useImperativeHandle` customizes the instance value that is exposed to parent components when using `ref`. As always, imperative code using refs should be avoided in most cases. `useImperativeHandle` should be used with `forwardRef`: diff --git a/content/docs/hooks-rules.md b/content/docs/hooks-rules.md index 698d1c741..8c332578c 100644 --- a/content/docs/hooks-rules.md +++ b/content/docs/hooks-rules.md @@ -40,7 +40,8 @@ npm install eslint-plugin-react-hooks ], "rules": { // ... - "react-hooks/rules-of-hooks": "error" + "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks + "react-hooks/exhaustive-deps": "warning" // Checks effect dependencies } } ``` From 83faa76713205d2c669005d6d51d29ee65079cf7 Mon Sep 17 00:00:00 2001 From: Victoria Quirante Date: Wed, 13 Mar 2019 16:28:05 +0100 Subject: [PATCH 09/22] Add React Alicante 2019 (#1819) --- content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/community/conferences.md b/content/community/conferences.md index 89ed871d0..e138b119a 100644 --- a/content/community/conferences.md +++ b/content/community/conferences.md @@ -76,6 +76,11 @@ September 26-28, 2019 in Goa, India [Website](https://www.reactindia.io/) - [Twitter](https://twitter.com/react_india) - [Facebook](https://www.facebook.com/ReactJSIndia) +### React Alicante 2019 {#react-alicante-2019} +September 26-28, 2019 in Alicante, Spain + +[Website](http://reactalicante.es/) - [Twitter](https://twitter.com/reactalicante) - [Facebook](https://www.facebook.com/ReactAlicante) + ## Past Conferences {#past-conferences} ### React.js Conf 2015 {#reactjs-conf-2015} From 110a318d1efc7d2c60644bd160dcac359b8778fb Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:38:51 +0000 Subject: [PATCH 10/22] Link to a data fetching demo --- content/docs/hooks-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 6145b968d..1f36f5f17 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/pwm32zx7z7) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} From 474de38b2c1e9dc8d9e6cc30b1cb0e8ff4ae3628 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:42:01 +0000 Subject: [PATCH 11/22] Use another example --- content/docs/hooks-faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 1f36f5f17..df6d9f7c3 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Here is a [small demo](https://codesandbox.io/s/pwm32zx7z7) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} @@ -571,7 +571,7 @@ We moved the function inside the effect so it doesn't need to be in its dependen >Tip > ->Check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +>Check out [this small demo](https://codesandbox.io/s/jvvkoo8pq3) and [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. **If for some reason you _can't_ move a function inside an effect, there are a few more options:** From 877b1897588c7c4640ecf1f6841adfe3986c44b8 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 15:46:05 +0000 Subject: [PATCH 12/22] Fix wording --- content/docs/hooks-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index df6d9f7c3..0540031ba 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -210,7 +210,7 @@ There are a few more heuristics, and they might change over time as we fine-tune ### How can I do data fetching with Hooks? -Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) to learn more about data fetching with Hooks. +Here is a [small demo](https://codesandbox.io/s/jvvkoo8pq3) to get you started. To learn more, check out [this article](https://www.robinwieruch.de/react-hooks-fetch-data/) about data fetching with Hooks. ### Is there something like instance variables? {#is-there-something-like-instance-variables} From 352c3ff01f437de2441533313bb1feb2c3f87414 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 13 Mar 2019 18:24:10 +0000 Subject: [PATCH 13/22] Fix example --- content/docs/hooks-faq.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 0540031ba..9016fa7f4 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -467,7 +467,7 @@ Yes. See [conditionally firing an effect](/docs/hooks-reference.html#conditional Generally speaking, no. ```js{3,8} -function Example() { +function Example({ someProp }) { function doSomething() { console.log(someProp); } @@ -481,7 +481,7 @@ function Example() { It's difficult to remember which props or state are used by functions outside of the effect. This is why **usually you'll want to declare functions needed by an effect *inside* of it.** Then it's easy to see what values from the component scope that effect depends on: ```js{4,8} -function Example() { +function Example({ someProp }) { useEffect(() => { function doSomething() { console.log(someProp); From 3e489fc2f9a028d570ccfdd74ddbc93faea27d32 Mon Sep 17 00:00:00 2001 From: Manuel Bieh Date: Thu, 14 Mar 2019 01:09:09 +0100 Subject: [PATCH 14/22] Fixing incorrect value for eslint rule (#1824) The name for the "warning" level in ESLint is `warn` not `warning` (https://eslint.org/docs/user-guide/configuring#configuring-rules) --- content/docs/hooks-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-rules.md b/content/docs/hooks-rules.md index 8c332578c..dbac9cffd 100644 --- a/content/docs/hooks-rules.md +++ b/content/docs/hooks-rules.md @@ -41,7 +41,7 @@ npm install eslint-plugin-react-hooks "rules": { // ... "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks - "react-hooks/exhaustive-deps": "warning" // Checks effect dependencies + "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies } } ``` From 40bbbd5b102cd33bdee85fcc4bd209dc06585975 Mon Sep 17 00:00:00 2001 From: Dimitar Nestorov Date: Thu, 14 Mar 2019 16:49:11 +0200 Subject: [PATCH 15/22] Updating useMemo docs (#1796) https://codesandbox.io/s/4x68k8nrw --- content/docs/hooks-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index 4466bc83b..4daf0f191 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -320,7 +320,7 @@ Pass a "create" function and an array of dependencies. `useMemo` will only recom Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`. -If no array is provided, a new value will be computed whenever a new function instance is passed as the first argument. (With an inline function, on every render.) +If no array is provided, a new value will be computed on every render. **You may rely on `useMemo` as a performance optimization, not as a semantic guarantee.** In the future, React may choose to "forget" some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without `useMemo` — and then add it to optimize performance. From 1fe2e0ae29b2fe8a8a09ab10048bb9fe284ff568 Mon Sep 17 00:00:00 2001 From: Nelson Reitz Date: Fri, 15 Mar 2019 15:04:19 +0100 Subject: [PATCH 16/22] Fix overflowing headers on small screens (#1830) --- src/theme.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/theme.js b/src/theme.js index 9f440d56d..a4d445206 100644 --- a/src/theme.js +++ b/src/theme.js @@ -89,6 +89,11 @@ const fonts = { lineHeight: '65px', fontWeight: 700, + [media.lessThan('small')]: { + overflowWrap: 'break-word', + wordBreak: 'break-word', + }, + [media.lessThan('medium')]: { fontSize: 40, lineHeight: '45px', From 877431a60bb64c4694d51cca79dfc4230d6af603 Mon Sep 17 00:00:00 2001 From: Arthur Denner Date: Sun, 17 Mar 2019 18:08:17 -0300 Subject: [PATCH 17/22] Fix previous article on hooks-effect.md (#1802) Hi! :wave: This PR only fixes the `Previous article` link on [this page](https://reactjs.org/docs/hooks-effect.html). Once this get merged, do I need to open a similar PR in each one of the translation repos? --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index fd7d89592..73d6a3d2c 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -3,7 +3,7 @@ id: hooks-state title: Using the Effect Hook permalink: docs/hooks-effect.html next: hooks-rules.html -prev: hooks-intro.html +prev: hooks-state.html --- *Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class. From 905a1134c07ea24aa4e4e4351371cf77b027280a Mon Sep 17 00:00:00 2001 From: Brian Mitchell Date: Mon, 18 Mar 2019 12:24:24 -0500 Subject: [PATCH 18/22] Add React Rally 2019 (#1840) --- content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/community/conferences.md b/content/community/conferences.md index e138b119a..ea8613c78 100644 --- a/content/community/conferences.md +++ b/content/community/conferences.md @@ -57,6 +57,11 @@ July 15-21, 2019. New York City, USA [Website](https://reactweek.nyc) - [Twitter](https://twitter.com/ReactWeek) +### React Rally 2019 +August 22-23, 2019. Salt Lake City, USA. + +[Website](https://www.reactrally.com/) - [Twitter](https://twitter.com/ReactRally) - [Instagram](https://www.instagram.com/reactrally/) + ### ComponentsConf 2019 {#componentsconf-2019} September 6, 2019 in Melbourne, Australia [Website](https://www.componentsconf.com.au/) - [Twitter](https://twitter.com/componentsconf) From 806da34988106c2a92c9784edc2bc0e91333ca4a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 19 Mar 2019 15:28:18 +0000 Subject: [PATCH 19/22] Add a DOM measurement recipe to Hooks FAQ (#1843) * Add a DOM measurement recipe to Hooks FAQ * Update content/docs/hooks-faq.md Co-Authored-By: gaearon * Update content/docs/hooks-faq.md Co-Authored-By: gaearon --- content/docs/hooks-faq.md | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 9016fa7f4..1e68b379c 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -41,6 +41,7 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo * [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops) * [Is there something like forceUpdate?](#is-there-something-like-forceupdate) * [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component) + * [How can I measure a DOM node?](#how-can-i-measure-a-dom-node) * [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean) * **[Performance Optimizations](#performance-optimizations)** * [Can I skip an effect on updates?](#can-i-skip-an-effect-on-updates) @@ -451,6 +452,60 @@ Try to avoid this pattern if possible. While you shouldn't need this often, you may expose some imperative methods to a parent component with the [`useImperativeHandle`](/docs/hooks-reference.html#useimperativehandle) Hook. +### How can I measure a DOM node? {#how-can-i-measure-a-dom-node} + +In order to measure the position or size of a DOM node, you can use a [callback ref](/docs/refs-and-the-dom.html#callback-refs). React will call that callback whenever the ref gets attached to a different node. Here is a [small demo](https://codesandbox.io/s/l7m0v5x4v9): + +```js{4-8,12} +function MeasureExample() { + const [height, setHeight] = useState(0); + + const measuredRef = useCallback(node => { + if (node !== null) { + setHeight(node.getBoundingClientRect().height); + } + }, []); + + return ( + <> +

Hello, world

+

The above header is {Math.round(height)}px tall

+ + ); +} +``` + +We didn't choose `useRef` in this example because an object ref doesn't notify us about *changes* to the current ref value. Using a callback ref ensures that [even if a child component displays the measured node later](https://codesandbox.io/s/818zzk8m78) (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements. + +Note that we pass `[]` as a dependency array to `useCallback`. This ensures that our ref callback doesn't change between the re-renders, and so React won't call it unnecessarily. + +If you want, you can [extract this logic](https://codesandbox.io/s/m5o42082xy) into a reusable Hook: + +```js{2} +function MeasureExample() { + const [rect, ref] = useClientRect(); + return ( + <> +

Hello, world

+ {rect !== null && +

The above header is {Math.round(rect.height)}px tall

+ } + + ); +} + +function useClientRect() { + const [rect, setRect] = useState(null); + const ref = useCallback(node => { + if (node !== null) { + setRect(node.getBoundingClientRect()); + } + }, []); + return [rect, ref]; +} +``` + + ### What does `const [thing, setThing] = useState()` mean? {#what-does-const-thing-setthing--usestate-mean} If you're not familiar with this syntax, check out the [explanation](/docs/hooks-state.html#tip-what-do-square-brackets-mean) in the State Hook documentation. From ac4aa6546e413bc6bdc6d8191081b739fbc6fb27 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 19 Mar 2019 17:12:44 +0000 Subject: [PATCH 20/22] Add more explanations to Hooks API page (#1845) * Add more explanations to API page * static * add info about useLayoutEffect * Update content/docs/hooks-reference.md Co-Authored-By: gaearon * nits * nit --- content/docs/hooks-faq.md | 4 ++-- content/docs/hooks-reference.md | 39 ++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/content/docs/hooks-faq.md b/content/docs/hooks-faq.md index 1e68b379c..7d2953293 100644 --- a/content/docs/hooks-faq.md +++ b/content/docs/hooks-faq.md @@ -908,7 +908,7 @@ function Form() { const [text, updateText] = useState(''); const textRef = useRef(); - useLayoutEffect(() => { + useEffect(() => { textRef.current = text; // Write it to the ref }); @@ -949,7 +949,7 @@ function useEventCallback(fn, dependencies) { throw new Error('Cannot call an event handler while rendering.'); }); - useLayoutEffect(() => { + useEffect(() => { ref.current = fn; }, [fn, ...dependencies]); diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index 4daf0f191..f2cc087d4 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -97,6 +97,8 @@ const [state, setState] = useState(() => { If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).) +Note that React may still need to render that specific component again before bailing out. That shouldn't be a concern because React won't unnecessarily go "deeper" into the tree. If you're doing expensive calculations while rendering, you can optimize them with `useMemo`. + ### `useEffect` {#useeffect} ```js @@ -173,12 +175,24 @@ The array of dependencies is not passed as arguments to the effect function. Con ### `useContext` {#usecontext} ```js -const context = useContext(Context); +const value = useContext(MyContext); ``` -Accepts a context object (the value returned from `React.createContext`) and returns the current context value, as given by the nearest context provider for the given context. +Accepts a context object (the value returned from `React.createContext`) and returns the current context value for that context. The current context value is determined by the `value` prop of the nearest `` above the calling component in the tree. + +When the nearest `` above the component updates, this Hook will trigger a rerender with the latest context `value` passed to that `MyContext` provider. -When the provider updates, this Hook will trigger a rerender with the latest context value. +Don't forget that the argument to `useContext` must be the *context object itself*: + + * **Correct:** `useContext(MyContext)` + * **Incorrect:** `useContext(MyContext.Consumer)` + * **Incorrect:** `useContext(MyContext.Provider)` + +>Tip +> +>If you're familiar with the context API before Hooks, `useContext(MyContext)` is equivalent to `static contextType = MyContext` in a class, or to ``. +> +>`useContext(MyContext)` only lets you *read* the context and subscribe to its changes. You still need a `` above in the tree to *provide* the value for this context. ## Additional Hooks {#additional-hooks} @@ -285,6 +299,8 @@ function Counter({initialCount}) { If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).) +Note that React may still need to render that specific component again before bailing out. That shouldn't be a concern because React won't unnecessarily go "deeper" into the tree. If you're doing expensive calculations while rendering, you can optimize them with `useMemo`. + ### `useCallback` {#usecallback} ```js @@ -356,7 +372,16 @@ function TextInputWithFocusButton() { } ``` -Note that `useRef()` is useful for more than the `ref` attribute. It's [handy for keeping any mutable value around](/docs/hooks-faq.html#is-there-something-like-instance-variables) similar to how you'd use instance fields in classes. +Essentially, `useRef` is like a "box" that can hold a mutable value in its `.current` property. + +You might be familiar with refs primarily as a way to [access the DOM](/docs/refs-and-the-dom.html). If you pass a ref object to React with `
`, React will set its `.current` property to the corresponding DOM node whenever that node changes. + +However, `useRef()` is useful for more than the `ref` attribute. It's [handy for keeping any mutable value around](/docs/hooks-faq.html#is-there-something-like-instance-variables) similar to how you'd use instance fields in classes. + +This works because `useRef()` creates a plain JavaScript object. The only difference between `useRef()` and creating a `{current: ...}` object yourself is that `useRef` will give you the same ref object on every render. + +Keep in mind that `useRef` *doesn't* notify you when its content changes. Mutating the `.current` property doesn't cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a [callback ref](/docs/hooks-faq.html#how-can-i-measure-a-dom-node) instead. + ### `useImperativeHandle` {#useimperativehandle} @@ -389,7 +414,11 @@ Prefer the standard `useEffect` when possible to avoid blocking visual updates. > Tip > -> If you're migrating code from a class component, `useLayoutEffect` fires in the same phase as `componentDidMount` and `componentDidUpdate`, so if you're unsure of which effect Hook to use, it's probably the least risky. +> If you're migrating code from a class component, note `useLayoutEffect` fires in the same phase as `componentDidMount` and `componentDidUpdate`. However, **we recommend starting with `useEffect` first** and only trying `useLayoutEffect` if that causes a problem. +> +>If you use server rendering, keep in mind that *neither* `useLayoutEffect` nor `useEffect` can run until the JavaScript is downloaded. This is why React warns when a server-rendered component contains `useLayoutEffect`. To fix this, either move that logic to `useEffect` (if it isn't necessary for the first render), or delay showing that component until after the client renders (if the HTML looks broken until `useLayoutEffect` runs). +> +>To exclude a component that needs layout effects from the server-rendered HTML, render it conditionally with `showChild && ` and defer showing it with `useEffect(() => { setShowChild(true); }, [])`. This way, the UI doesn't appear broken before hydration. ### `useDebugValue` {#usedebugvalue} From b1bc193dbad464f7c659b10f3f57e37e265a063e Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 19 Mar 2019 22:09:21 +0000 Subject: [PATCH 21/22] Document useContext bailout strategy (#1848) --- content/docs/hooks-reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index f2cc087d4..98c2e14c0 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -188,6 +188,8 @@ Don't forget that the argument to `useContext` must be the *context object itsel * **Incorrect:** `useContext(MyContext.Consumer)` * **Incorrect:** `useContext(MyContext.Provider)` +A component calling `useContext` will always re-render when the context value changes. If re-rendering the component is expensive, you can [optimize it by using memoization](https://github.com/facebook/react/issues/15156#issuecomment-474590693). + >Tip > >If you're familiar with the context API before Hooks, `useContext(MyContext)` is equivalent to `static contextType = MyContext` in a class, or to ``. From 31b67da40ae965f315777cb47fcd1db39a260622 Mon Sep 17 00:00:00 2001 From: Amirata Khodaparast Date: Thu, 21 Mar 2019 09:50:22 -0400 Subject: [PATCH 22/22] Add .vscode directory to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d1bde99ce..420e5874f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .idea node_modules public -yarn-error.log \ No newline at end of file +yarn-error.log +.vscode \ No newline at end of file