diff --git a/documentation/blog/2024-09-17-chakra-ui.md b/documentation/blog/2024-09-17-chakra-ui.md index edd3faad9a31..c350b03c9c4f 100644 --- a/documentation/blog/2024-09-17-chakra-ui.md +++ b/documentation/blog/2024-09-17-chakra-ui.md @@ -8,6 +8,8 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-12-19-chakra-ui/ hide_table_of_contents: false --- +**This article was last updated on September 17, 2024, to expand usage of Chakra UI.** + ## Introduction [Chakra UI](https://chakra-ui.com/) has attracted a lot of attention over the years due to its versatility, ease of use, and customizable approach to integrating into any front-end application. diff --git a/documentation/blog/2023-12-29-admin-panel-frameworks.md b/documentation/blog/2024-09-18-admin-panel-frameworks.md similarity index 75% rename from documentation/blog/2023-12-29-admin-panel-frameworks.md rename to documentation/blog/2024-09-18-admin-panel-frameworks.md index 01aa409bac15..8d1d94f83d2d 100644 --- a/documentation/blog/2023-12-29-admin-panel-frameworks.md +++ b/documentation/blog/2024-09-18-admin-panel-frameworks.md @@ -8,6 +8,10 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-12-29-admin-pane hide_table_of_contents: false --- +**This article was last updated on September 18, 2024, to add sections on Accessibility Features, Performance Optimization, and SEO Considerations for Admin Panels and Dashboards.** + +This version reflects the potential updates related to the admin panel content. + ## Introduction Admin panels and dashboards are a vital component in various products, serving as a centralized hub for monitoring, analyzing, and managing different types of data. These dashboards generalize data in a defined manner, providing users with a comprehensive overview of ongoing activities. They are instrumental for organizations, offering extensive insights, analytics, metrics, and control over all operational aspects. Admin panels have demonstrated their value in numerous processes such as customer database management, inventory management, and bug tracking. They have played a significant role in businesses by simplifying complex processes and facilitating data-driven strategies. @@ -122,8 +126,8 @@ It also offers a wide range of performance features that ensure the smooth runni **Community and Support** -Refine.js has a large and active community of developers. Currently, It has more than 17,500 stars and 1400 forks on GitHub. -It has a Discord server with 3000+ people. +Refine.js has a large and active community of developers. Currently, It has more than 27.5K+ stars and 2K+ forks on GitHub. +It has a Discord server with 6000+ people. ## [2.Angular - ngx-admin](https://github.com/akveo/ngx-admin) @@ -336,9 +340,83 @@ It uses ASP.NET Core authentication routines for client identity. It also offers Blazor apps are highly performant, Blazor has a whole array of performance tooling to make your apps fast. You can set up caching, rate limiting, object reuse, and response compression, to scale up your app. -**Community and Support** +## Is SEO Optimization Important in Admin Panels? + +Recently, I have been thinking a lot about how much SEO applies to admin panels and whether it matters. Typically, SEO isn’t a top priority for admin panels because they are mostly internal tools. But in some cases, it can still be relevant. Let me explain when SEO matters and how we can optimize for it. + +SEO optimization is not critical for most admin panels, since these tools are designed for internal use and aren’t intended to be indexed or ranked by search engines. However, in some specific cases, it may be important: + +### Public-Facing Apps + +If some parts of the admin panel, such as help sections, documentation, or dashboards, are publicly visible, SEO optimization is necessary to ensure those pages are indexed correctly and rank well on search engines. + +### Content Visibility + +If there are reports or analytics dashboards that need to be publicly available, SEO helps ensure those pages are discoverable by users. + +### Performance Considerations + +While performance optimizations—like page speed and mobile responsiveness—aren’t directly related to SEO for internal panels, improving these factors enhances user experience. If we have public-facing elements, optimizing for speed will impact both usability and SEO. + +### **SEO Optimization Tips for Admin Panels** + +Here are some tips for optimizing admin panels for SEO, particularly in cases where parts of the panel may be publicly accessible: + +### Meta Tags + +We should include proper meta tags like title, description, and Open Graph for any public-facing pages. This will ensure those pages appear well in search results or when shared on social media. Frameworks like **Next.js** (used with Refine.js) have built-in support for server-side rendering (SSR), which is great for SEO. + +### Server-Side Render + +Frameworks like **Next.js** and **Angular** offer built-in SSR, which is important for SEO. SSR ensures that content is rendered on the server, allowing search engines to crawl and index pages more smoothly. While Vue and Blazor can use SSR with additional setup, it requires more effort for SEO purposes. + +### Page Speed + +Fast-loading admin panels improve user experience, and for public pages, they also impact SEO. We can optimize images, use lazy loading, and process JavaScript efficiently to speed up page loads. Lightweight frameworks like **Blazor** and **Vuetify** help keep load times fast, which positively affects SEO for public pages. + +### Content Indexing + +If parts of the admin panel are public, like documentation, FAQs, or help sections, we should ensure they are indexed correctly by search engines. Clean URLs, sitemaps, and proper use of **robots.txt** help search engines find the right pages. Frameworks like **Refine.js** and **Angular** include built-in SEO support, while **Laravel Nova** and **Django Admin** may require more manual effort. + +### Mobile-Friendly + +Although admin panels are usually accessed from desktops, some users may view them on mobile. Ensuring responsiveness will improve user experience and impact SEO if any parts of the panel are public. **Vuetify** and **ngx-admin** offer good mobile responsiveness out of the box. + +### Structured Data + +Adding structured data (like JSON-LD) helps search engines better understand the content of public pages. This is useful if dashboards showing key stats or updates are publicly visible. **Next.js** and **Angular** make adding structured data easier for improved search visibility. + +### When It’s Not Important? + +For admin panels that are used purely internally, SEO is usually not relevant since these pages don’t need to be indexed by search engines. In these cases, the main focus should be on performance optimization and user experience rather than SEO. + +## Bonus: Accessibility Features in Admin Panel Frameworks + +I've been looking into some of the admin panel frameworks we're considering and how they handle accessibility features. Accessibility is important, especially if we want to make sure our application is available to any users, including those with disabilities. Here is a quick rundown: + +### Refine.js + +Refine doesn't have baked-in support for accessibility, but since it's based on React, we can use React's tooling and ARIA attributes to ensure a level of accessibility. For keyboard navigation and screen reader support, we'd have to handle that manually, but there are a number of React libraries that make it easier. + +### ngx-admin (Angular) + +Angular itself has strong support for accessibility, including built-in ARIA attributes, which is a big plus. ngx-admin follows best practices, providing keyboard navigation and screen reader compatibility right out of the box. + +### Vuetify (Vue) + +Vuetify is highly focused on accessibility. It has built-in tools for ARIA support and was designed with screen readers and keyboard navigation in mind. It also comes with helper components that make all form controls, buttons, and other elements highly accessible. + +### Laravel Nova + +By default, Nova doesn't have many accessibility features, but since it's based on Laravel and PHP, we can implement custom solutions. We just need to manually include ARIA attributes and test for keyboard navigation to ensure everything works smoothly. + +### Django Admin + +Django Admin isn't very accessibility-friendly by default, as it's mainly designed for internal use. We'd need to implement most of the accessibility features ourselves, such as support for screen readers and proper labeling. + +### Blazor -Blazor being a Microsoft-owend app is in the .NET community. As of now, the .NET has more than 450,000 stars on Github, and more than 345,000 members on Meetup. +As part of the .NET ecosystem, Blazor does prioritize accessibility standards, but we'd still need to adapt it for our specific needs. Blazor provides some built-in ARIA support and good keyboard navigation features. ## Conclusion diff --git a/documentation/blog/2024-01-17-uri-vs-url.md b/documentation/blog/2024-09-18-uri-vs-url.md similarity index 74% rename from documentation/blog/2024-01-17-uri-vs-url.md rename to documentation/blog/2024-09-18-uri-vs-url.md index efccfb4006e9..3d70fabcb073 100644 --- a/documentation/blog/2024-01-17-uri-vs-url.md +++ b/documentation/blog/2024-09-18-uri-vs-url.md @@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-02-01-uri-vs-url hide_table_of_contents: false --- -**_This article was last updated on January 17, 2024 to provide a more detailed explanation, image and comparison table of URI VS URL._** +**This article was last updated on September 18, 2024, to add sections on the Historical Context of URI and URL, Security Considerations, and SEO Implications of Proper URI and URL Usage.** ## Introduction @@ -24,31 +24,24 @@ In this article, you will learn about the concept of a URI, its components, its Steps we'll cover: -- [What is URI?](#what-is-uri) -- [Components of URI](#components-of-uri) -- [Functions and Architecture of URI](#functions-and-architecture-of-uri) -- [Examples of URI](#examples-of-uri) -- [Syntax Of URI](#syntax-of-uri) -- [Use Case of URI](#use-case-of-uri) - [What is URL?](#what-is-url) -- [Examples of URLs](#examples-of-urls) -- [Benefits of URI over URL](#benefits-of-uri-over-url) -- [Coding Smarter: Using URI & URL Knowledge in Development](#coding-smarter-using-uri--url-knowledge-in-development) -- [Comparison Summary of URL and URI](#comparison-summary-of-url-and-uri) + - [Examples of URLs](#examples-of-urls) + - [Benefits of URI over URL](#benefits-of-uri-over-url) + - [Coding Smarter: Using URI \& URL Knowledge in Development](#coding-smarter-using-uri--url-knowledge-in-development) + - [Security Considerations for Using URLs and URIs](#security-considerations-for-using-urls-and-uris) + - [Comparison Summary of URL and URI](#comparison-summary-of-url-and-uri) + - [Bonus: SEO Benefits of Proper URI and URL Usage](#bonus-seo-benefits-of-proper-uri-and-url-usage) + - [Conclusion](#conclusion) ## What is URI? -URI is an abbreviation for "Uniform Resource Identifier," which refers to a unique identifier composed of a string of characters that points to any resource on the internet via procedures such as name, location, or both. +Back in the late 1980s, Tim Berners-Lee, the inventor of the World Wide Web, introduced the concept of the URI (Uniform Resource Identifier). His goal was to create a universal system to locate and access web resources, from web pages to files, using a unique identifier. URIs were designed to identify any resource on the web, whether by name, location, or both. -URLs and URNs are the two types of URIs. URLs, a type of URI, stand for Uniform Resource Locators and are commonly known as website addresses, illustrating a key aspect of the difference between URI and URL. They specify the location of a resource on the Internet and instruct a web browser on how to retrieve it. +URLs (Uniform Resource Locators), a specific type of URI, focus on the "location" aspect—where a resource can be found on the web and how to retrieve it. For instance, the URI "https://www.mywebsite.com" specifies the location of a website and instructs a web browser to retrieve it using the HTTPS protocol. This makes URLs crucial for pinpointing resources by their address, while URIs, in general, can identify resources without necessarily specifying how to locate them. -For example, the URI "https://www.mywebsite.com" specifies the location of a website and instructs a web browser to retrieve it using the HTTPS protocol. +Web protocols like HTTP and HTTPS rely on URIs to communicate with resources on the internet. The architecture of a URI is a string of characters that represent a web resource's address, often combining the resource’s name and location. -Web protocols such as HTTP and HTTPS use URIs to communicate with multiple resources on the internet. The architecture of URIs is a string of characters that represents the address of a web resource, which is usually a combination of the resource's name and address. This distinction is crucial in understanding what is a URI and how it functions in contrast to a URL - -In essence, a URI can identify any type of resource, including web pages, images, videos, and other types of files. They are an essential component of how the Internet works and are used in various contexts, including web addresses, links, and other types of references to online resources. - -Understanding what a URI is, including URI examples, is fundamental in grasping the URI vs URL concept +In essence, URIs are a broad concept for identifying resources on the internet, and URLs are a more specific type of URI focused on location. While these terms are often used interchangeably, this historical context helps clarify why URLs are just one way of identifying resources within the broader URI framework. ## Components of URI @@ -195,6 +188,39 @@ URIs (Uniform Resource Identifiers) are a broader classification of identifiers 4. **Routing in Web Applications**: URLs are integral in routing within web applications. Frameworks like React or Angular use URLs to determine which component to render, for example, `https://mywebsite.com/about` might route to an About page. 5. **Link Generation**: In content management systems, URLs are dynamically generated to link to various content pieces. A blog post might be accessible through a URL like `https://blog.mywebsite.com/2024/01/my-first-post`, which is automatically generated based on the post's title and date. +## Security Considerations for Using URLs and URIs + +I wanted to share a quick note on some important **security considerations** when working with URLs and URIs, especially since we use them all the time. + +**Exposing Sensitive Data** + URLs can expose sensitive information like user IDs or session tokens, especially if they're passed in the query string. This can be dangerous if someone captures the URL. + +It’s better to avoid passing sensitive data in the URL and use POST requests instead. Also, always use HTTPS to encrypt the data. + +**Phishing and Spoofing** + Attackers can create fake URLs that look like real ones (for example, "g00gle.com" instead of "google.com"). Users might click these without noticing the difference. + +Make sure we validate URLs properly and teach users to check links before clicking. + +**Open Redirects** + Sometimes URLs can allow users to be redirected to another website, which can be exploited for phishing. + +Always check and limit where redirects can send users. + +**Cross-Site Scripting (XSS)** + If a URL includes unsanitized user input, attackers could inject malicious scripts into our web pages. + +Always sanitize input and encode URL parameters to block script injections. + +**Session Hijacking** + Passing session tokens or login info in a URL can be risky because they can be logged or shared accidentally. + +It’s safer to use cookies for session management and secure them with HTTP-only and secure flags. + +**Using HTTPS** + Always ensure that any sensitive data is sent over HTTPS to keep it encrypted and safe from attackers. + **What we can do:** Double-check that all critical pages use HTTPS, especially when handling personal data. + ## Comparison Summary of URL and URI | URL | URI | @@ -221,6 +247,28 @@ Below you can find a comparison table summarizing both the similarities and diff | Mutability | Generally mutable and can change over time. | Can be either mutable or immutable, depending on whether it's a URL or a URN. | | Similarity | All URLs are URIs. | Includes URLs as a subset. | -# Conclusion +## Bonus: SEO Benefits of Proper URI and URL Usage + +I just wanted to share some quick thoughts on how using **URIs and URLs** the right way can improve **SEO** on our website. Here are the key points to consider: + +**Clean and Descriptive URLs** + Search engines like Google give more preference to URLs that are readable and include target keywords. For example, “**/blog/seo-best-practices**” is much better than “**/blog/id123?ref=xyz**.” Clean URLs make it easier for both users and search engines to understand what the page is about. + +**Structural Consistency** + Keeping a consistent URL structure across the website helps with crawlability, allowing search engines to index pages correctly. For example, using a pattern like “**/category/post-name**” creates a clear structure for the content hierarchy. + +**Avoiding Duplicates with Canonical URLs** + When we have multiple URLs pointing to the same content (common with tracking parameters), we can use **canonical tags** to tell search engines which one should be considered the main URL. This helps avoid duplicate content issues, which can negatively impact rankings. + +**Use Hyphens, Not Underscores** + Hyphens in URLs (like **/web-design-tips**) are better for SEO than underscores (like **/web_design_tips**) because search engines treat hyphens as spaces between words, making the URL more readable. + +**Short, Keyword-Rich URLs** + URLs should be short and include important keywords without overstuffing. Shorter URLs tend to rank better because they’re easier to remember and share. + +**HTTPS for Better Ranking** + Search engines prioritize websites using HTTPS over HTTP, so using secure URLs not only improves security but also helps with SEO ranking. + +## Conclusion In summary, when considering URI vs URL, URIs are more versatile and flexible than URLs. They are designed to be more persistent and interoperable. URIs are more general-purpose identifiers that can identify any type of resource, whereas URLs are limited to identifying a resource's location on the Internet. diff --git a/documentation/blog/2024-01-16-react-memo.md b/documentation/blog/2024-09-19-react-memo.md similarity index 72% rename from documentation/blog/2024-01-16-react-memo.md rename to documentation/blog/2024-09-19-react-memo.md index de272b33c2c1..358aa7c0372f 100644 --- a/documentation/blog/2024-01-16-react-memo.md +++ b/documentation/blog/2024-09-19-react-memo.md @@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2022-09-13-react-memo hide_table_of_contents: false --- -**_This article was last updated on January 16, 2024 to reflect the latest changes to the React memo API and to provide a more detailed explanation of how React.memo() works._** +**This article was last updated on September 19, 2024, to add sections on Deep vs Shallow Comparison in Memoization, Profiling Components in DevTools, and Best Practices for Using `React.memo()`.** ## Introduction @@ -22,18 +22,14 @@ Steps we'll cover in this post: - [What is Memoization?](#what-is-memoization) - [Why Memoization in React?](#why-memoization-in-react) - - [Excessive Re-rendering Due to Ancestor Re-rendering](#excessive-re-rendering-due-to-ancestor-re-rendering) - - [Expensive Utilities](#expensive-utilities) - - [Passing Callbacks to Children](#passing-callbacks-to-children) - [Memoization in React](#memoization-in-react) - [About the React Memoization Series](#about-the-react-memoization-series) +- [Project Overview](#project-overview) - [Memoizing a Functional Component using `React.memo()`](#memoizing-a-functional-component-using-reactmemo) - - [What is `React.memo` ?](#what-is-reactmemo-) - - [React.memo() - How to Memoize Component Props](#reactmemo---how-to-memoize-component-props) - - [When to Use `React.memo`](#when-to-use-reactmemo) - - [When Not to Use `React.memo`](#when-not-to-use-reactmemo) - - [React.memo: Prop Comparison](#reactmemo-prop-comparison) - - [Using React Memo with Custom Comparators](#using-react-memo-with-custom-comparators) +- [Best Practices for Using `React.memo()`](#best-practices-for-using-reactmemo) +- [You can Profile Components in DevTools](#you-can-profile-components-in-devtools) +- [Bonus: Deep vs Shallow Comparison in Memoization](#bonus-deep-vs-shallow-comparison-in-memoization) +- [Live Example](#live-example) ## What is Memoization? @@ -356,6 +352,89 @@ When we click on the `Sign Out` button in the navbar, we can see in the console This is now because **React memo** caches the props passed to the component and checks for incoming changes. Notice the Boolean value of `signedIn` printed to the console. A change in `signedIn`'s state renews the memoization and a re-render of the component is triggered. +## Best Practices for Using `React.memo()` + +I wanted to share a few best practices for using `React.memo()` with some code examples. These can help improve the performance of our React components: + +### Use it for frequently re-rendered components + +If a component is being re-rendered unnecessarily due to its parent re-rendering, `React.memo()` can help. For example, if we have a component like this: + +```jsx +const Post = ({ title, content }) => { + console.log("Rendering Post component"); + return ( +
+

{title}

+

{content}

+
+ ); +}; + +export default Post; +``` + +We can prevent unnecessary re-renders by wrapping it with `React.memo()`: + +```jsx +const Post = React.memo(({ title, content }) => { + console.log("Rendering Post component"); + return ( +
+

{title}

+

{content}

+
+ ); +}); + +export default Post; +``` + +Now, this component will only re-render if its `title` or `content` props change. + +### Avoid overusing `React.memo()` + +It’s important not to use `React.memo()` everywhere. If a component’s props change frequently, memoization can add more overhead than improvement. Use it only when the props don't change often. + +### Shallow comparison only + +By default, `React.memo()` performs a shallow comparison of props. If you’re passing complex objects or arrays, you might need to write a custom comparison function. For example: + +```jsx +const Post = React.memo( + ({ title, content }) => { + console.log("Rendering Post component"); + return ( +
+

{title}

+

{content}

+
+ ); + }, + (prevProps, nextProps) => { + return prevProps.title === nextProps.title; // Custom comparison + }, +); +``` + +In this example, we are only checking if the `title` has changed. The component will only re-render if the `title` prop changes, even if `content` changes. + +### Don’t memoize static or rarely updated components + +If a component is static or doesn’t receive changing props, there’s no need to use `React.memo()`. For example, this component doesn’t benefit from memoization: + +```jsx +const Footer = () => { + return ; +}; + +// No need to memoize here +``` + +Memoization would add unnecessary complexity without improving performance. + +By following these best practices and using `React.memo()` in the right situations, we can optimize performance without adding extra overhead. + ### When to Use `React.memo` This is actually what we want. Because we don't want `` to re-render when we don't need it to, and we want to re-render it when we need it to. @@ -416,6 +495,115 @@ Here, we are omitting `signedIn` from the comparison by including only `post`. N This is because, our `customComparator` checks for equality of incoming values of only `post` and excludes `signedIn` from the comparison. +## You can Profile Components in DevTools + +I wanted to share some tips on **Profiling Components in DevTools** to help us identify and fix performance bottlenecks in our React app. + +**Opening the React Profiler** + +- Install the **React Developer Tools** extension for Chrome or Firefox. +- Open **DevTools** (`F12` or `Ctrl + Shift + I`), go to the **Profiler** tab, and click **"Record"** before interacting with the app. + +**Capturing Component Renders** + +Interact with the app while recording (e.g., clicking buttons, changing state). The Profiler will track which components re-render and how long each takes. + +**Analyzing Results** + +After recording, view the timeline to inspect: + +- **Render Time**: Time taken for each component render. +- **Render Reason**: Prop or state changes causing re-renders. + +**Optimizing Slow Renders** + +If a component (e.g., `Post`) is re-rendering unnecessarily, wrap it in `React.memo()` to prevent it from re-rendering when props haven't changed. + +**Highlight Updates** + +Enable **"Highlight Updates"** in the **React** tab to visually see components that re-render, making it easier to spot unnecessary updates. + +Using the Profiler, we can quickly identify and optimize slow re-renders. + +## Bonus: Deep vs Shallow Comparison in Memoization + +I wanted to go over deep vs shallow comparison in memoization and how that affects performance optimization in React, especially when using `React.memo()` and other memoization techniques. + +### Shallow Comparison + +By default, React does **shallow comparison** to check if a component’s props have changed to decide whether to re-render it. Shallow comparison means React checks for changes only at the top level of an object or array and doesn't go into nested properties. + +For example: + +```jsx +const person1 = { name: "John" }; +const person2 = { name: "John" }; + +console.log(person1 === person2); // false - because of different object references +``` + +Even though `person1` and `person2` have the same data, during a shallow comparison, they are considered different because the reference is compared, not the content. + +```jsx +const Post = React.memo(({ title, content }) => { + console.log("Rendering Post component"); + return ( +
+

{title}

+

{content}

+
+ ); +}); +``` + +If the `title` and `content` props are **primitives** (like strings or numbers), the shallow comparison works as expected. But if they are **objects** or **arrays**, even a slight change in the reference (e.g., creating a new object) will trigger a re-render. + +### Deep Comparison + +A deep comparison goes beyond top-level properties and checks all nested properties. It's a bit heavier since every level of the object or array has to be checked. + +React doesn't perform deep comparisons by default in `React.memo()` because it can be slow, especially with deeply nested objects or large arrays. + +### Custom Comparators for Deep Comparison + +If shallow comparison isn’t enough (for example, when passing complex objects as props), we can provide a custom comparator function in `React.memo()` to implement **deep comparison**. + +Here’s a custom comparator for deep comparison: + +```jsx +import React from "react"; +import { isEqual } from "lodash"; + +const Post = ({ post }) => { + console.log("Rendering Post component"); + return ( +
+

{post.title}

+

{post.content}

+
+ ); +}; + +const customComparator = (prevProps, nextProps) => { + return isEqual(prevProps.post, nextProps.post); // Deep comparison using lodash +}; + +export default React.memo(Post, customComparator); +``` + +Here, we’re using `lodash`'s `isEqual()` function to deep compare the entire `post` object. This helps avoid unnecessary re-renders when only the reference changes, but the data inside remains the same. + +### When to Use Deep Comparison + +- **Complex Data Structures**: When passing large or deeply nested objects as props, and you don’t want to re-render components unnecessarily. +- **Performance Trade-offs**: Deep comparison might be slower than a shallow comparison, so it’s important to measure and ensure that the performance gain from avoiding re-renders outweighs the cost of deep comparison. + +### Best Practices + +- Use shallow comparison whenever possible to keep performance high. +- Only use deep comparison when you’re sure the props involve deeply nested objects that don’t change often. +- Use libraries like `lodash` or `deep-equal` for effective deep comparisons. + ## Summary In this post, we acknowledged what memoization is and why it is important in React. We learned about the use of `React.memo()`, `useMemo` and `useCallback` APIs for implementing memoization in a React app. diff --git a/documentation/blog/2024-01-17-react-use-memo.md b/documentation/blog/2024-09-19-react-use-memo.md similarity index 68% rename from documentation/blog/2024-01-17-react-use-memo.md rename to documentation/blog/2024-09-19-react-use-memo.md index ad300a3d9028..eb9a2fa01b28 100644 --- a/documentation/blog/2024-01-17-react-use-memo.md +++ b/documentation/blog/2024-09-19-react-use-memo.md @@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2022-09-16-react-use- hide_table_of_contents: false --- -**_This article was last updated on January 17, 2024 to reflect the latest changes to the React useMemo API and to provide a more explanations of how React.useMemo() works._** +**This article was last updated on September 19, 2024, to add sections on the latest changes to the React useMemo API and provide more detailed explanations of how `useMemo()` works.** ## Introduction @@ -26,13 +26,13 @@ In this post, we dive into the details of the **useMemo** hook with an extension Steps we'll cover: -- [What is React useMemo?](#what-is-react-usememo-) - - [Resource Intensive Functions in React: Why Use React `useMemo`?](#resource-intensive-functions-in-react-why-use-react-usememo-) +- [What is React useMemo ?](#what-is-react-usememo-) - [Optimizing Expensive Utility Functions with React `useMemo` Hook](#optimizing-expensive-utility-functions-with-react-usememo-hook) - [React useMemo: How to Cache the Value of Expensive Utilities](#react-usememo-how-to-cache-the-value-of-expensive-utilities) - [Using React useMemo with Dependencies](#using-react-usememo-with-dependencies) - - [When to Use React `useMemo()`](#when-to-use-react-usememo) - - [When Not to Use React `useMemo`](#when-not-to-use-react-usememo) +- [More Use Cases for `useMemo()` Hook](#more-use-cases-for-usememo-hook) +- [Bonus: Best Practices for Using `useMemo()` in React](#bonus-best-practices-for-using-usememo-in-react) +- [Live Example](#live-example) ## What is React useMemo ? @@ -131,9 +131,9 @@ const Blog = ({ signedIn }) => { return (
-

Memoization in React

+

Memoization in React

-
+

Latest posts

{localTime}
@@ -258,6 +258,178 @@ It is also important to know that **useMemo** returns a value, as opposed to a f `useMemo()` is preferred for memoizing a value rather than a callback function. We should not use `useMemo` for memoizing a function such as a callback. +## More Use Cases for `useMemo()` Hook + +I wanted to share some advanced use cases for the `useMemo()` hook that might be useful in our projects. I’ve added some code examples for clarity. + +### Handling Large Data Processing + +It's great for situations when we’re working with large datasets (like sorting or filtering) and want to avoid recalculating the data when it hasn’t changed. Here’s an example of sorting a big list: + +```javascript +import React, { useMemo } from "react"; + +const LargeDataComponent = ({ data }) => { + const sortedData = useMemo(() => { + console.log("Sorting data..."); + return data.sort((a, b) => a.value - b.value); + }, [data]); + + return ( +
+ {sortedData.map((item) => ( +
{item.name}
+ ))} +
+ ); +}; +``` + +In this case, `sortedData` will only be recalculated when `data` changes. Otherwise, the cached sorted result is reused. + +### Improving API Responses + +We can use `useMemo()` to cache API responses, so we avoid making the same API calls multiple times with the same parameters. Here's how: + +```javascript +import React, { useMemo, useState, useEffect } from "react"; + +const fetchData = async (query) => { + const response = await fetch(`https://api.example.com/data?search=${query}`); + return response.json(); +}; + +const APIComponent = ({ query }) => { + const [data, setData] = useState([]); + + const memoizedData = useMemo(() => { + return fetchData(query); + }, [query]); + + useEffect(() => { + memoizedData.then((result) => setData(result)); + }, [memoizedData]); + + return ( +
+ {data.map((item) => ( +
{item.name}
+ ))} +
+ ); +}; +``` + +Here, `useMemo()` stores the API response based on the `query`. The API will only be called again when the `query` changes, saving resources. + +### Pagination and Data Display + +`useMemo()` can also be useful for pagination to cache already loaded pages of data, avoiding unnecessary recalculations when flipping through pages. Here’s an example: + +```javascript +import React, { useMemo, useState } from "react"; + +const PaginationComponent = ({ data, currentPage, itemsPerPage }) => { + const paginatedData = useMemo(() => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return data.slice(startIndex, endIndex); + }, [currentPage, data, itemsPerPage]); + + return ( +
+ {paginatedData.map((item) => ( +
{item.name}
+ ))} +
+ ); +}; +``` + +Here, we are only recalculating the data for the current page and not reprocessing the entire dataset each time the page changes. + +## Bonus: Best Practices for Using `useMemo()` in React + +I thought I'd share some best practices for using `useMemo()` in React to help optimize our components without overcomplicating the code. Here are a few key points: + +### Use `useMemo()` Only for Expensive Calculations + +The main purpose of `useMemo()` is to avoid re-executing expensive functions unnecessarily. If the calculation isn't costly performance-wise, it's better not to use `useMemo()`, as it adds extra complexity. + +```javascript +const sortedData = useMemo(() => { + return data.sort((a, b) => a.value - b.value); +}, [data]); +``` + +Here, sorting large data is expensive, so memoizing the result is useful. However, if the operation is lightweight, `useMemo()` isn't necessary. + +### Avoid Overusing `useMemo()` + +Using `useMemo()` too much can make the code harder to read and maintain. The rule of thumb is to only apply it where it truly improves performance. + +#### Example (Not needed): + +```javascript +const simpleCalculation = useMemo(() => { + return number * 2; +}, [number]); +``` + +In this case, multiplying a number by 2 is inexpensive, and using `useMemo()` here would be unnecessary. + +### Always Include Dependencies Properly + +Ensure that all necessary dependencies are included in the array to prevent unwanted bugs. `useMemo()` will only recalculate when one of the dependencies changes, so missing a dependency can lead to stale data or incorrect behavior. + +#### Example: + +```javascript +const filteredData = useMemo(() => { + return data.filter((item) => item.active); +}, [data]); // Always ensure the dependency (data) is included +``` + +### Don’t Use `useMemo()` to Memoize Functions + +`useMemo()` is for memoizing values, not functions. If you need to memoize a function, use the `useCallback()` hook instead. + +#### Correct Example (using `useCallback()`): + +```javascript +const handleClick = useCallback(() => { + console.log("Button clicked"); +}, []); +``` + +### Use it for Expensive Object or Array Creation + +If you’re creating complex objects or arrays in your component, especially those that involve computations, `useMemo()` can help by memoizing the created object or array so it’s not recreated on every render. + +```javascript +const userPreferences = useMemo(() => { + return { theme: "dark", language: "en" }; +}, []); +``` + +### Use `useMemo()` for Derived State + +When the state is derived from props or other states, `useMemo()` can prevent unnecessary recalculations. + +```javascript +const fullName = useMemo(() => { + return `${firstName} ${lastName}`; +}, [firstName, lastName]); +``` + +Here, `useMemo()` ensures that `fullName` is only recalculated when `firstName` or `lastName` changes. + +- Use `useMemo()` for expensive calculations (like sorting, filtering). +- Avoid overusing it—only apply where performance gain is significant. +- Always include dependencies accurately. +- Use `useCallback()` for memoizing functions, not `useMemo()`. +- Apply `useMemo()` for heavy object or array creation. + ## Summary In this post, we demonstrated the use of React `useMemo()` hook and examined how it plays a crucial role in optimizing the performance of a component by memoizing an expensive utility function. We noticed that it is important to specify the dependencies of **useMemo** so that the memo is re-computed and renewed when the state of the dependencies change. diff --git a/documentation/docs/core/refine-component/index.md b/documentation/docs/core/refine-component/index.md index 7a37f1664748..0ce6de0a8cea 100644 --- a/documentation/docs/core/refine-component/index.md +++ b/documentation/docs/core/refine-component/index.md @@ -720,6 +720,8 @@ Callback to handle all live events. ## ~~catchAll~~ +| 🚨 Use the `` component in your routes instead. + When the app is navigated to a non-existent route, Refine shows a default error page. A custom error component can be used for this error page by passing the customized component to the `catchAll` property: ```tsx title="App.tsx" @@ -736,6 +738,8 @@ const App = () => ( ## ~~LoginPage~~ +| 🚨 Use the `` component in your routes instead. + Refine has a default login page form which is served on the `/login` route when the `authProvider` configuration is provided. Custom login component can be passed to the `LoginPage` property. @@ -754,6 +758,8 @@ const App = () => ( ## ~~DashboardPage~~ +| 🚨 Use the `` component in your routes instead. + A custom dashboard page can be passed to the `DashboardPage` prop which is accessible on the root route. The dashboard item will appear at the top of the sider menu. If `DashboardPage` is not given, the first resource of `resources` will be shown. @@ -790,12 +796,16 @@ const App = () => ( ## ~~Sider~~ +| 🚨 Use `Sider` prop of `` component instead. + The default sidebar can be customized by using Refine hooks and passing custom components to the `Sider` property. > For more information, refer to the [`useMenu` hook documentation →](/docs/core/hooks/utilities/use-menu) ## ~~Footer~~ +| 🚨 Use `Footer` prop of `` component instead. + The default app footer can be customized by passing the `Footer` property. ```tsx title="App.tsx" @@ -812,7 +822,7 @@ const App = () => ( ## ~~Header~~ -The default app header can be customized by passing the `Header` property. +| 🚨 Use `Header` prop of `` component instead. ```tsx title="App.tsx" // highlight-next-line @@ -828,6 +838,8 @@ const App = () => ( ## ~~Layout~~ +| 🚨 Use `` as children of `` instead. + The default layout can be customized by passing the `Layout` property. Layout property will receive individual layout components as property. @@ -873,6 +885,8 @@ const App = () => ( ## ~~OffLayoutArea~~ +| 🚨 Use `OffLayoutArea` prop of `` component instead. + The component wanted to be placed out of the app layout structure can be set by passing to the `OffLayoutArea` prop. ```tsx title="App.tsx" @@ -888,6 +902,8 @@ const App = () => ( ## ~~Title~~ +| 🚨 Use `Title` prop of `` component instead. + The app title can be set by passing the `Title` property. ```tsx title="App.tsx" diff --git a/documentation/docs/data/data-provider/index.md b/documentation/docs/data/data-provider/index.md index 1ee9f34b50d0..ba7ad94fe047 100644 --- a/documentation/docs/data/data-provider/index.md +++ b/documentation/docs/data/data-provider/index.md @@ -703,7 +703,7 @@ Refine will consume: ### How can I customize existing data providers? -[Refer to the "Create Data Provider with Swizzle" section in the tutorial for more information →][swizzle-a-data-provider] +[You can swizzle the data provider using the Refine CLI and customize it as needed.][swizzle-a-data-provider] ### How I can override a specific method of Data Providers? @@ -732,9 +732,9 @@ const myDataProvider = { ``` [basekey]: /docs/core/interface-references#basekey -[create-a-data-provider]: /docs/data/data-provider -[swizzle-a-data-provider]: /docs/packages/cli -[data-provider-tutorial]: /docs/data/data-provider +[create-a-data-provider]: https://refine.dev/tutorial/essentials/data-fetching/intro/#creating-a-data-provider +[swizzle-a-data-provider]: /docs/packages/cli/#swizzle +[data-provider-tutorial]: https://refine.dev/tutorial/essentials/data-fetching/intro/ [use-api-url]: /docs/data/hooks/use-api-url [use-create]: /docs/data/hooks/use-create [use-create-many]: /docs/data/hooks/use-create diff --git a/documentation/docs/data/packages/simple-rest/index.md b/documentation/docs/data/packages/simple-rest/index.md index d66d36e58638..ad7faa06ec9c 100644 --- a/documentation/docs/data/packages/simple-rest/index.md +++ b/documentation/docs/data/packages/simple-rest/index.md @@ -18,14 +18,21 @@ Simple REST package exports a function that accepts `apiUrl` and `httpClient` pa ```tsx title="app.tsx" import { Refine } from "@refinedev/core"; -// highlight-next-line +// highlight-start import dataProvider from "@refinedev/simple-rest"; +import axios from "axios"; +// highlight-end + +// highlight-start +const httpClient = axios.create(); const App = () => { return ( ")} + // highlight-start + // `httpClient` is optional. + dataProvider={(dataProvider(""), httpClient)} + // highlight-end /* ... */ /> ); diff --git a/documentation/src/refine-theme/landing-hero-section.tsx b/documentation/src/refine-theme/landing-hero-section.tsx index 0d9a23d2435c..5892851b3543 100644 --- a/documentation/src/refine-theme/landing-hero-section.tsx +++ b/documentation/src/refine-theme/landing-hero-section.tsx @@ -106,7 +106,7 @@ export const LandingHeroSection = ({ className }: { className?: string }) => { > - Get started + See Docs { return ( { { x: 12, y: 174, - width: 200, - height: 344, + width: 204, + height: 360, render: - "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr/sider.png", + "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr2/menu.png", codePosition: "right", code: ` - import { useMenu } from "@refinedev/core"; - import Link from "next/link"; - - const { menuItems } = useMenu(); - - return menuItems.map((item) => ( - - {item.icon} - {item.label} - - )); - `, + import { useMenu, Link, CanAccess } from "@refinedev/core"; + import { List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material"; + + const Sider = () => { + const { menuItems, selectedKey } = useMenu(); + return ( + + {menuItems.map((item) => ( + + + + {item.icon} + {item.label} + + + + ))} + + ); + }; + `, }, { x: 788, - y: 184, - width: 332, - height: 260, + y: 254, + width: 356, + height: 296, render: - "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr/poll.png", + "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr2/poll.png", codePosition: "left", code: ` - import { useList } from "@refinedev/core"; + import { useList } from "@refinedev/core"; - const { data } = useList({ - resource: "polls", - filters: [ - { field: "is_active", operator: "eq", value: true }, - ], - pagination: { current: 1, pageSize: 1 } - }); + const { data } = useList({ + resource: "polls", + filters: [{ field: "status", operator: "eq", value: "active" }], + pagination: { current: 1, pageSize: 1 }, + liveMode: "auto", + }); `, }, { - x: 736, - y: 24, - width: 384, - height: 112, + x: 978, + y: 22, + width: 166, + height: 36, render: - "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr/timer.png", + "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr2/request-time-off.png", codePosition: "left", code: ` - import { useGetIdentity, useUpdate } from "@refinedev/core"; - - const { data: { activeTaskId } } = useGetIdentity(); + import { CreateButton } from "@refinedev/mui"; + import { TimeOffIcon } from "@/icons"; - const { mutate } = useUpdate(); - - const onBreak = () => mutate({ - resource: "tasks", - id: activeTaskId, - values: { is_paused: true }, - }); + }> + Request Time Off + + `, + }, + { + x: 552, + y: 78, + width: 284, + height: 128, + render: + "https://refine.ams3.cdn.digitaloceanspaces.com/website/static/showcase-images/hr2/sick-leave.png", + codePosition: "left", + code: ` + import { useGetIdentity, useList } from "@refinedev/core"; + + const { data: { employeeId } } = useGetIdentity(); + + const { data } = useList({ + resource: "time-offs", + pagination: { current: 1, pageSize: 1 }, + filters: [ + { + field: "employeeId", + operator: "eq", + value: employeeId, + }, + { + field: "status", + operator: "eq", + value: "approved", + }, + { + field: "type", + operator: "eq", + value: "sick-leave", + }, + ], + }); + const totalSickLeave = data?.total; `, }, ]} diff --git a/packages/chakra-ui/src/components/buttons/show/index.tsx b/packages/chakra-ui/src/components/buttons/show/index.tsx index 16d18e7ce0b1..c2e67be4b515 100644 --- a/packages/chakra-ui/src/components/buttons/show/index.tsx +++ b/packages/chakra-ui/src/components/buttons/show/index.tsx @@ -12,7 +12,7 @@ import type { ShowButtonProps } from "../types"; /** * `` uses Chakra UI {@link https://chakra-ui.com/docs/components/button `