- vừa được xem lúc

Blog#92: The Ugliest Pattern in React: When and Why to Use it

0 0 28

Người đăng: NGUYỄN ANH TUẤN

Theo Viblo Asia

Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.

React is a popular JavaScript library for building user interfaces, and it's known for its efficiency and simplicity. However, there is one pattern in React that stands out as particularly ugly: updating state in response to rendering. In this article, we'll take a closer look at this pattern, when you might need to use it, and some alternatives that may be more suitable in certain situations.

What is the Ugliest Pattern in React?

In React, you typically update state in event handlers. However, there are rare cases where you might want to adjust state in response to rendering - for example, if you want to change a state variable when a prop changes. Here's an example of this pattern in action:

function CountLabel({ count }) { const [prevCount, setPrevCount] = useState(count); const [trend, setTrend] = useState(null); if (prevCount !== count) { setPrevCount(count); setTrend(count > prevCount ? 'increasing' : 'decreasing'); } return ( <> <h1>{count}</h1> {trend && <p>The count is {trend}</p>} </> );
}

As you can see, this code sets state in the render function, which goes against the principle that the render function should be pure. This can make the code difficult to understand and is generally best avoided.

When to Use the Ugliest Pattern in React

So, when should you use this pattern? According to the React documentation, there are a few cases where it might be necessary:

If the value you need can be computed entirely from the current props or other state, you can remove the redundant state altogether. If you want to reset the entire component tree's state, you can pass a different key to your component. If you can, update all the relevant state in event handlers. If none of these conditions apply and you still need to update state in response to rendering, then you may have to use the ugliest pattern in React.

Examples

Now that we've covered the basics of this pattern, let's look at a few examples of when you might need to use it.

Example 1: Updating a Progress Bar

Imagine you have a progress bar component that displays the percentage of a task that has been completed. The percentage is passed to the component as a prop, and the component should update the width of the progress bar to reflect the percentage. Here's how you might use the ugliest pattern in React to achieve this:

function ProgressBar({ percentage }) { const [prevPercentage, setPrevPercentage] = useState(percentage); const [width, setWidth] = useState(0); if (prevPercentage !== percentage) { setPrevPercentage(percentage); setWidth(percentage); } return ( <div style={{ width: `${width}%` }}> {percentage}% complete </div> );
}

Example 2: Displaying a Loading Indicator

Suppose you have a component that fetches data from an API and displays it to the user. While the data is being fetched, you want to display a loading indicator to let the user know that something is happening. Here's how you might use the ugliest pattern in React to achieve this:

function DataTable() { const [prevLoading, setPrevLoading] = useState(true); const [loading, setLoading] = useState(true); const [data, setData] = useState([]); if (prevLoading !== loading) { setPrevLoading(loading); } useEffect(() => { async function fetchData() { setLoading(true); const response = await fetch('https://my-api.com/data'); const data = await response.json(); setData(data); setLoading(false); } fetchData(); }, []); return ( <div> {loading && <p>Loading...</p>} {data.length > 0 && ( <table> <thead> <tr> <th>Name</th> <th>Age</th> <th>Location</th> </tr> </thead> <tbody> {data.map(item => ( <tr key={item.id}> <td>{item.name}</td> <td>{item.age}</td> <td>{item.location}</td> </tr> ))} </tbody> </table> )} </div> );
}

In this example, we use the useEffect hook to fetch data from an API when the component mounts. While the data is being fetched, we set the loading state to true, which causes the loading indicator to be displayed. When the data has been successfully fetched, we set the loading state to false, which causes the loading indicator to be hidden and the data to be displayed in a table.

It's worth noting that the ugliest pattern in React is not the only way to display a loading indicator in this situation. You could also use the useEffect hook to update the loading state when the data is being fetched, or you could use the shouldComponentUpdate lifecycle method to prevent unnecessary re-renders. Ultimately, the best approach will depend on your specific needs and the requirements of your application.

Example 3: Displaying a Notification

Imagine you have a notification component that displays a message to the user. The message is passed to the component as a prop, and the component should update the message when the prop changes. Here's how you might use the ugliest pattern in React to achieve this:

function Notification({ message }) { const [prevMessage, setPrevMessage] = useState(message); const [displayMessage, setDisplayMessage] = useState(null); if (prevMessage !== message) { setPrevMessage(message); setDisplayMessage(message); } return ( <div> {displayMessage && <p>{displayMessage}</p>} </div> );
}

Example 4: Updating a Form Field

Suppose you have a form component that allows the user to enter their email address. The component should update the email address when the user types in the input field. Here's how you might use the ugliest pattern in React to achieve this:

function EmailForm() { const [prevEmail, setPrevEmail] = useState(''); const [email, setEmail] = useState(''); if (prevEmail !== email) { setPrevEmail(email); } return ( <form> <label> Email: <input type="email" value={email} onChange={event => setEmail(event.target.value)} /> </label> </form> );
}

Example 5: Updating a Navbar

Imagine you have a navbar component that displays the current page the user is on. The current page is passed to the component as a prop, and the component should highlight the corresponding nav item when the prop changes. Here's how you might use the ugliest pattern in React to achieve this:

function Navbar({ currentPage }) { const [prevPage, setPrevPage] = useState(currentPage); const [activeItem, setActiveItem] = useState(null); if (prevPage !== currentPage) { setPrevPage(currentPage); setActiveItem(currentPage); } return ( <nav> <ul> <li className={activeItem === 'home' ? 'active' : ''}>Home</li> <li className={activeItem === 'about' ? 'active' : ''}>About</li> <li className={activeItem === 'contact' ? 'active' : ''}>Contact</li> </ul> </nav> );
}

Alternatives to the Ugliest Pattern in React

While the ugliest pattern in React can be useful in certain situations, it's generally best to avoid it if possible. Here are a few alternatives you might consider:

  1. Use the useEffect hook to update state in response to prop changes. This is the recommended approach, but it can cause unnecessary re-renders if not used carefully.
  2. Use the shouldComponentUpdate lifecycle method to prevent unnecessary re-renders. This is a good option if you need more control over when the component updates, but it can make your code less readable.
  3. Use a memoization library like reselect to compute derived data from props and state. This can help to avoid unnecessary re-renders and make your code more efficient.

Conclusion

The ugliest pattern in React is a pattern that should be used with caution. While it can be useful in certain situations, it's generally best to avoid it if possible and use alternatives like useEffect or memoization instead. Remember to carefully consider your options and choose the approach that makes the most sense for your specific use case.

As always, I hope you enjoyed this article and learned something new. Thank you and see you in the next articles!

If you liked this article, please give me a like and subscribe to support me. Thank you. 😊

Ref

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 525

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 153

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 145

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 110

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 245