From d6a6ea65c414a7edf5d06329acc8c72e097a79fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Necati=20=C3=96zmen?= Date: Thu, 26 Sep 2024 11:20:31 +0300 Subject: [PATCH 1/3] docs(blog): update tdd post. (#6368) --- ...tdd-vs-bdd.md => 2024-09-24-tdd-vs-bdd.md} | 180 +++++++++++++++++- 1 file changed, 173 insertions(+), 7 deletions(-) rename documentation/blog/{2024-01-25-tdd-vs-bdd.md => 2024-09-24-tdd-vs-bdd.md} (76%) diff --git a/documentation/blog/2024-01-25-tdd-vs-bdd.md b/documentation/blog/2024-09-24-tdd-vs-bdd.md similarity index 76% rename from documentation/blog/2024-01-25-tdd-vs-bdd.md rename to documentation/blog/2024-09-24-tdd-vs-bdd.md index 1154e39efc74..f06ce8d75f18 100644 --- a/documentation/blog/2024-01-25-tdd-vs-bdd.md +++ b/documentation/blog/2024-09-24-tdd-vs-bdd.md @@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-06-09-tdd-vs-bdd hide_table_of_contents: false --- -**_This article was last updated on January 25, 2024 to add comparison table and more detailed usecases of TDD and BDD_** +**_This article was last updated on September 24, 2024 to include advanced TDD and BDD techniques, common pitfalls, and how to avoid them._** ## Introduction @@ -21,15 +21,15 @@ In this article, you’ll learn about Test-Driven Development (TDD) and Behavior Steps we'll cover: - [Overview of Test-Driven Development](#overview-of-test-driven-development) - - [Pros and cons of TDD](#pros-and-cons-of-tdd) - [Step-by-step demo example of TDD implementation](#step-by-step-demo-example-of-tdd-implementation) - [Overview of Behavior-Driven Development](#overview-of-behavior-driven-development) - - [BDD testing](#bdd-testing) - - [Pros and cons of BDD](#pros-and-cons-of-bdd) - [Step-by-step demo example of BDD implementation](#step-by-step-demo-example-of-bdd-implementation) -- [When to use BDD (Behavior-Driven Development)](#when-to-use-bdd-behavior-driven-development) -- [When to use TDD (Test-Driven Development)](#when-to-use-tdd-test-driven-development) +- [When to use BDD (Behavior-Driven Development):](#when-to-use-bdd-behavior-driven-development) +- [When to use TDD (Test-Driven Development):](#when-to-use-tdd-test-driven-development) +- [Common Pitfalls and How I Avoid Them in TDD](#common-pitfalls-and-how-i-avoid-them-in-tdd) +- [Common Pitfalls and How I Avoid Them in BDD](#common-pitfalls-and-how-i-avoid-them-in-bdd) - [Comparison of TDD and BDD](#comparison-of-tdd-and-bdd) +- [Bonus: Advanced TDD and BDD Techniques](#bonus-advanced-tdd-and-bdd-techniques) ## Overview of Test-Driven Development @@ -364,6 +364,75 @@ The tests will pass once you add the appropriate code to implement the scenarios - **Regulatory compliance and documentation**: In environments where there is a need for detailed documentation of the development process for regulatory compliance, TDD can provide an audit trail of tests that correspond to specific requirements of the system. - **Continuous integration environments**: TDD is ideal in continuous integration environments where tests are run frequently. It ensures that new code does not break existing functionality, thus facilitating continuous improvement of the code with minimal disruption. +## Common Pitfalls and How I Avoid Them in TDD + +**Too Many Small Tests are Being Written** +This often gets me caught up in writing tests for every little function or method. It feels so complete, but it's really just slowing me down and making the test suite a lot more maintenance-heavy. + +**How I avoid it:** I now test the whole system most of the time for its behavior rather than testing all internal details. + +```javascript +// Cannot test private or tiny methods. +test('should add item to cart', () => +const cart = new ShoppingCart(); +cart.addItem({ name: 'Book', price: 10 }); +expect(cart.items.length).toBe(1); +); +END +``` + +**No Refactoring** + +I had often been tempted, once it passed, to just move on and skip cleaning up the code. But not refactoring usually begets messy, hard-to-maintain code later on. + +Always take the time to refactor immediately after getting the test to pass. That's why TDD's "Red, Green, Refactor" is so important. + +**Writing Code Before Tests** +This is also you can often find me writing the code first, then subsequently adding tests for; it's here I've realized that indeed buries the advantages of TDD. + +Now, I write the test always first—even if it takes some extra time upfront. This way, I can catch bugs quickly and remain focused on my goal. + +## Common Pitfalls and How I Avoid Them in BDD + +**Too General or Too Specific Writing Scenarios** +I notice that both very vague and very highly detailed writing of scenarios leads to confusion: where they are vague, they miss important behavior; where they are too detailed, it is overwhelming. + +I strive to focus my attention on the writing of scenarios that describe users' behavior in free text, striking a balance between the level of detail and clarity. + +```gherkin +Scenario: User logs in successfully +Given the user is on the login page +When the user inputs correct credentials +He will then be taken to the dashboard. +``` + +**Not Involving Non-Technical Stakeholders** +I had written some BDD scenarios in the past when I did not collaborate enough with the product owners or stakeholders. The scenarios weren't really aligned with the business need in those cases. + +I make sure to involve product owners and nontechnical stakeholders up-front because this means that scenarios reflect real business goals. + +**Not Automating BDD Tests** + +I have from time to time written good BDD scenarios yet didn't automate them, which led to missed tests and manual errors. + +**How I avoid it:** I always use tools like Cucumber in order to automate the BDD tests—so all go smooth and consistent. + +```javascript +END // Example of BDD scenario automation using Cucumber.js +const { Given, When, Then } = require('@cucumber/cucumber'); + +Given('the user is on the login page', function () + +// code to divert to the login page + +}) + +When('the user enters valid credentials', function () { + +//Credentials Input Code END Then('the user is redirected to the dashboard', function () // code to check the redirecting to dashboard ); +END +``` + ## Comparison of TDD and BDD So far, you've learned what TDD and BDD are, what they entail, and how they work. Let's look at how they differ in various aspects, as shown in the table below: @@ -387,6 +456,103 @@ So far, you've learned what TDD and BDD are, what they entail, and how they work
-# Conclusion +## Bonus: Advanced TDD and BDD Techniques + +Lately, I have been looking at some of the more advanced TDD and BDD techniques. For me, these have made the difference, mainly in controlling some complex test cases. + +### Advanced TDD Techniques + +**Mocking and Stubbing** + Whenever I have code that depends on something external, like a database or an API, testing gets really tricky. Mocks and stubs help me feel like I'm really insulating what I'm trying to test. + +I utilize mocking frameworks in order to simulate some external dependencies so that the tests will stay fast and focused. + +```javascript +const fetchData = require("./fetchData"); +const axios = require("axios"); + +jest.mock("axios"); + +test("should fetch user data", async () => { + const user = { name: "John" }; + axios.get.mockResolvedValue({ data: user }); + + const result = await fetchData(); + expect(result.name).toBe("John"); +}); +``` + +In the following example, the `axios.get` method is mocked away from useEffect in order to simulate an API request for testing the behavior of `fetchData`. + +**Testing Edge Cases and Exceptions** + TDD is great for ensuring that code works as expected, but it's equally important to test how it will handle unexpected or invalid inputs. I always try to write tests that cover the edge cases of exceptions. + +I write tests for unexpected input values, null values, or edge cases that might not occur frequently but may cause the application to break. + +```javascript +test("should return an error when there is no data", () => { + const func = () => processData(null); + expect(func).toThrow("Invalid data"); +}); +``` + +I am testing here how my function handles a null input. This should raise an error. + +### Advanced BDD Techniques + +**Summarizing the Case** + In my experience with BDD, scenario outlines can definitely help in reducing repeated tests for the same scenario with different values in data. + +Scenario outlines allow you to write a single test structure and run it multiple times with different inputs. This will save you time and clean up the scenarios. + +```gherkin +Scenario Outline: Verifying user input +Given that "" are the user inputs +When the form is submitted by the user +Then the message "" shall appear + +Examples: + | input | message | + | John | Input is valid | + | "" | Input is required | + | 123 | Error Input | +``` + +In this scenario, running the scenario a few times with different inputs helps me concisely cover a number of different cases. + +**Selective Testing Using Tags** + When working on big projects, running all BDD tests may take much time. In BDD, I have started using tags to run selectively only the scenarios in focus. + +I label scenarios based on priority or feature so at development time I can run selected sets of tests. + +```gherkin +@login +Scenario: User has logged in successfully + Given the user is on the login page + When the user enters valid credentials + Then the user is redirected to the dashboard +``` + +In practice, what it means is that, at any given time, working on the login functionality, I run only the tests tagged as having `@login` but not all tests. + +**BDD with Continuous Integration** + BDD tests incorporated into the CI pipeline changed my professional life. It ensured that the scenarios are tested automatically after every code change. + +I have configured BDD tools such as Cucumber to run as part of CI processes so that the behavior is continually validated. + +```yaml +# Example CI pipeline with BDD tests +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run BDD tests + run: npm run cucumber +``` + +These BDD tests can be used in automation form within the CI pipeline to catch potential behavioral issues quite early, and also ensure at all times that the code behaves as per expectation. + +## Conclusion Finally, you've reached the end of this article, where you learned about Test-Driven Development (TDD) and Behavior-Driven Development (BDD), including what they entail, their principles, their benefits and drawbacks, and how they differ. You also saw TDD and BDD in action in a demo application. From a97c018d9f4f82fa3e59172ab80db98fb62cea8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Necati=20=C3=96zmen?= Date: Thu, 26 Sep 2024 11:20:53 +0300 Subject: [PATCH 2/3] Update grp post (#6367) --- ...-vs-rest.md => 2024-09-24-grpc-vs-rest.md} | 405 +++++++++++++++++- 1 file changed, 402 insertions(+), 3 deletions(-) rename documentation/blog/{2024-01-22-grpc-vs-rest.md => 2024-09-24-grpc-vs-rest.md} (66%) diff --git a/documentation/blog/2024-01-22-grpc-vs-rest.md b/documentation/blog/2024-09-24-grpc-vs-rest.md similarity index 66% rename from documentation/blog/2024-01-22-grpc-vs-rest.md rename to documentation/blog/2024-09-24-grpc-vs-rest.md index eb418c267b77..57a7ad1ed9e3 100644 --- a/documentation/blog/2024-01-22-grpc-vs-rest.md +++ b/documentation/blog/2024-09-24-grpc-vs-rest.md @@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2023-06-23-grpc-vs-re hide_table_of_contents: false --- -**_This article was last updated on January 22, 2024 to add more comparison and usecase information for gRPC vs REST to provide a more clear insight_** +**_This article was last updated on September 24, 2024 to include migration strategies, security considerations, and real-world examples to provide a more comprehensive comparison between gRPC and REST._** ## Introduction @@ -26,11 +26,13 @@ Steps we'll cover: - [What is gRPC?](#what-is-grpc) - [How gRPC APIs work](#how-grpc-apis-work) - [How developers can use gRPC and REST](#how-developers-can-use-grpc-and-rest) +- [Security Considerations](#security-considerations) + - [Authentication and Authorization](#authentication-and-authorization) + - [Data Encryption](#data-encryption) - [Similarities and differences between gRPC and REST](#similarities-and-differences-between-grpc-and-rest) - - [Similarities between gRPC and REST](#similarities-between-grpc-and-rest) - - [Differences between gRPC and REST](#differences-between-grpc-and-rest) - [Comparison summary](#comparison-summary) - [When to use REST and when to use gRPC](#when-to-use-rest-and-when-to-use-grpc) +- [Bonus: Migration Strategies](#bonus-migration-strategies) ## Understanding APIs @@ -276,6 +278,161 @@ Below illustration will help you understand how both these technologies work. - **Simple CRUD operations:** REST is well-suited for standard web APIs that perform CRUD (Create, Read, Update, Delete) operations. For example, a web service for a book inventory system can use RESTful APIs for adding new books, retrieving book details, updating prices, or deleting old records. - **Building public APIs for broader reach:** When developing public APIs intended for a wide range of clients, including third-party developers, REST is often preferred due to its simplicity and widespread familiarity. An example is a social media platform providing a RESTful API for fetching user profiles or posting messages, allowing easy integration for a variety of external services and applications. +## Security Considerations + +When choosing between gRPC and REST, it's important to understand how each handles security. Both offer ways to manage authentication, authorization, and data encryption, but they do it differently. + +### Authentication and Authorization + +#### gRPC + +- **SSL/TLS Support:** gRPC uses SSL/TLS by default to secure communication between the client and server. This means that data is encrypted and protected from eavesdropping. + +- **Mutual Authentication:** gRPC supports mutual SSL/TLS authentication, where both the client and server present certificates to verify each other. + +- **Token-Based Authentication with Metadata:** You can pass tokens or credentials using metadata in gRPC calls. + + _Example in Python:_ + + ```python + # Client-side code + import grpc + from your_proto_pb2_grpc import YourServiceStub + from your_proto_pb2 import YourRequest + + def run(): + with grpc.secure_channel('localhost:50051', grpc.ssl_channel_credentials()) as channel: + stub = YourServiceStub(channel) + metadata = [('authorization', 'Bearer your_token')] + response = stub.YourMethod(YourRequest(), metadata=metadata) + print(response) + ``` + +- **Custom Authentication:** You can implement custom authentication mechanisms using interceptors or middleware. + +#### REST + +- **OAuth 2.0 Integration:** REST APIs often use OAuth 2.0 for authentication and authorization. This allows secure, token-based access. + + _Example in Node.js with Express:_ + + ```javascript + // Server-side code + const express = require("express"); + const app = express(); + const jwt = require("jsonwebtoken"); + + app.use(express.json()); + + function authenticateToken(req, res, next) { + const authHeader = req.headers["authorization"]; + const token = authHeader && authHeader.split(" ")[1]; + + if (token == null) return res.sendStatus(401); + + jwt.verify(token, "your_secret_key", (err, user) => { + if (err) return res.sendStatus(403); + req.user = user; + next(); + }); + } + + app.get("/protected", authenticateToken, (req, res) => { + res.json({ message: "This is protected data." }); + }); + + app.listen(3000, () => console.log("Server running on port 3000")); + ``` + +- **API Keys and Tokens:** REST can use API keys, JSON Web Tokens (JWT), and other methods to control access. + +- **HTTP Basic and Digest Authentication:** These methods are simpler but should be used over HTTPS to be secure. + +### Data Encryption + +#### gRPC + +- **Encrypted Communication:** gRPC ensures all communication is encrypted using SSL/TLS. + +- **Protocol Buffers Serialization:** Data is serialized using Protocol Buffers, which is efficient and not human-readable. However, this is not encryption, so sensitive data should still be sent over secure channels. + + _Example of a `.proto` file:_ + + ```protobuf + syntax = "proto3"; + + service YourService { + rpc YourMethod (YourRequest) returns (YourResponse); + } + + message YourRequest { + string data = 1; + } + + message YourResponse { + string result = 1; + } + ``` + +- **Server Implementation in Go:** + + ```go + // Server-side code in Go + import ( + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "log" + "net" + ) + + func main() { + creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key") + if err != nil { + log.Fatalf("Failed to load certificates: %v", err) + } + + server := grpc.NewServer(grpc.Creds(creds)) + // Register your services here + + lis, err := net.Listen("tcp", ":50051") + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + + if err := server.Serve(lis); err != nil { + log.Fatalf("Failed to serve: %v", err) + } + } + ``` + +#### REST + +- **HTTPS Usage:** REST APIs typically use HTTPS to encrypt data between the client and server. + + ```javascript + // Server-side code + const express = require("express"); + const https = require("https"); + const fs = require("fs"); + + const app = express(); + + // Define your routes here + + const options = { + key: fs.readFileSync("server.key"), + cert: fs.readFileSync("server.cert"), + }; + + https.createServer(options, app).listen(3000, () => { + console.log("HTTPS server running on port 3000"); + }); + ``` + +- **Data Formats:** REST often uses JSON or XML, which are human-readable. While HTTPS secures the data in transit, sensitive data should be handled carefully. + +- **Additional Encryption:** For extra security, you can encrypt sensitive data within the payload before sending it. + ## Similarities and differences between gRPC and REST ### Similarities between gRPC and REST @@ -311,6 +468,248 @@ The architectural style of REST API differs from that of gRPC API due to their d Use REST when you are developing web application and a more standardized approach with wide compatibility. REST is ideal for CRUD operations and when simplicity and caching are priorities. Go for gRPC if you are building application with high-performance requirements, efficient communication in microservices, and for systems where language interoperability and network efficiency are critical, such as in real-time communication. +## Bonus: Migration Strategies + +Transitioning from REST to gRPC can offer performance improvements and enhanced functionality. Here's a guide to help developers migrate their existing REST APIs to gRPC smoothly. + +### Step-by-Step Guide to Migrating from REST to gRPC + +1. **Assess Your Current API Structure** + + - **Inventory Endpoints**: List all your REST endpoints and their functionalities. + - **Identify Core Services**: Determine which services will benefit most from gRPC's performance improvements. + +2. **Define Protobuf Messages and Services** + + - **Create `.proto` Files**: Define your data structures and services using Protocol Buffers. + + ```protobuf + syntax = "proto3"; + + package example; + + service UserService { + rpc GetUser (GetUserRequest) returns (GetUserResponse); + } + + message GetUserRequest { + int32 user_id = 1; + } + + message GetUserResponse { + int32 user_id = 1; + string name = 2; + string email = 3; + } + ``` + +3. **Generate gRPC Code from Protobuf Definitions** + + - Use the `protoc` compiler to generate server and client code in your chosen language. + + ```bash + protoc --go_out=plugins=grpc:. *.proto + ``` + +4. **Implement the gRPC Services** + + - **Server-Side Implementation**: Write the server logic for your gRPC services. + + _Example in Go:_ + + ```go + // Server-side code + import ( + "context" + "net" + + "google.golang.org/grpc" + pb "path/to/your/protobuf/package" + ) + + type server struct { + pb.UnimplementedUserServiceServer + } + + func (s *server) GetUser(ctx context.Context, in *pb.GetUserRequest) (*pb.GetUserResponse, error) { + // Business logic to retrieve user + return &pb.GetUserResponse{ + UserId: in.UserId, + Name: "John Doe", + Email: "john.doe@example.com", + }, nil + } + + func main() { + lis, _ := net.Listen("tcp", ":50051") + s := grpc.NewServer() + pb.RegisterUserServiceServer(s, &server{}) + s.Serve(lis) + } + ``` + +5. **Update Clients to Use gRPC** + + - **Client-Side Implementation**: Rewrite client applications to communicate with the gRPC server. + + _Example in Python:_ + + ```python + # Client-side code + import grpc + from example_pb2 import GetUserRequest + from example_pb2_grpc import UserServiceStub + + def run(): + with grpc.insecure_channel('localhost:50051') as channel: + stub = UserServiceStub(channel) + response = stub.GetUser(GetUserRequest(user_id=123)) + print("User Details:", response) + + if __name__ == '__main__': + run() + ``` + +### Maintain REST Endpoints During Transition + +In a smooth transition from REST to gRPC, one would maintain the existing endpoint for REST and introduce the gRPC services in parallel. An API Gateway would route clients to either the REST service or the gRPC service, depending on the needs of the client. For backward compatibility, onboarding the clients one at a time with minimum disruption would be done, and API versioning handles the different stages of that process. + +Unit tests and integration tests should be provided for any new gRPC services that are written. Compare their performance to REST endpoints for assurance of functionality. Deployment: With Docker, containerize your services or use similar tooling to gain a consistent approach. As changes are integrated into the CI/CD pipeline, automation will test and deploy. + +### Potential Pitfalls and How to Avoid Them + +- **Incomplete Protobuf Definitions**: Ensure all data models are accurately defined in your `.proto` files to prevent serialization issues. +- **Network Configuration**: gRPC uses HTTP/2, so make sure your infrastructure supports it. +- **Security Concerns**: Implement SSL/TLS for encrypted communication, similar to how you secure REST APIs. + +### Case Study: Migrating a RESTful User Service to gRPC + +Let's consider a simple user service that retrieves user information. + +**Original REST Endpoint (Node.js with Express):** + +```javascript +// Server-side code +const express = require("express"); +const app = express(); + +app.get("/user/:id", (req, res) => { + const userId = req.params.id; + // Logic to get user data + res.json({ + user_id: userId, + name: "John Doe", + email: "john.doe@example.com", + }); +}); + +app.listen(3000, () => console.log("REST API running on port 3000")); +``` + +**Migrated gRPC Service (Node.js):** + +1. **Define the `.proto` File** + + ```protobuf + syntax = "proto3"; + + package example; + + service UserService { + rpc GetUser (GetUserRequest) returns (GetUserResponse); + } + + message GetUserRequest { + int32 user_id = 1; + } + + message GetUserResponse { + int32 user_id = 1; + string name = 2; + string email = 3; + } + ``` + +2. **Implement the Server** + + ```javascript + // Server-side code + const grpc = require("@grpc/grpc-js"); + const protoLoader = require("@grpc/proto-loader"); + + const packageDefinition = protoLoader.loadSync("example.proto"); + const proto = grpc.loadPackageDefinition(packageDefinition).example; + + function getUser(call, callback) { + const userId = call.request.user_id; + // Logic to get user data + callback(null, { + user_id: userId, + name: "John Doe", + email: "john.doe@example.com", + }); + } + + const server = new grpc.Server(); + server.addService(proto.UserService.service, { GetUser: getUser }); + server.bindAsync( + "0.0.0.0:50051", + grpc.ServerCredentials.createInsecure(), + () => { + server.start(); + console.log("gRPC server running on port 50051"); + }, + ); + ``` + +3. **Implement the Client** + + ```javascript + // Client-side code + const grpc = require("@grpc/grpc-js"); + const protoLoader = require("@grpc/proto-loader"); + + const packageDefinition = protoLoader.loadSync("example.proto"); + const proto = grpc.loadPackageDefinition(packageDefinition).example; + + const client = new proto.UserService( + "localhost:50051", + grpc.credentials.createInsecure(), + ); + + client.GetUser({ user_id: 123 }, (error, response) => { + if (error) { + console.error("Error:", error); + } else { + console.log("User Details:", response); + } + }); + ``` + +### Tools to Assist Migration + +- **gRPC-Gateway**: Allows you to expose gRPC services as REST endpoints, helping in gradual migration. + + _Example usage in Go:_ + + ```go + // Install gRPC-Gateway + go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway + + // Generate reverse-proxy + protoc -I . example.proto --grpc-gateway_out=. + + // Run the gateway server + ``` + +- **API Gateways**: Tools like Envoy or Kong can route traffic between REST and gRPC services. + +### Benefits Achieved After Migration + +- **Improved Performance**: Lower latency and higher throughput with gRPC. +- **Efficient Data Transfer**: Smaller payload sizes with Protocol Buffers. +- **Bi-Directional Streaming**: Enhanced capabilities not available in REST. + ## Conclusion Finally, you've reached the end of the article, where you learned about the architectural style of REST APIs and gRPC APIs, including how they work and the differences between their distinctive features. From 1fa7b334540d4aac7108fd47846a73afa1f14762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Necati=20=C3=96zmen?= Date: Thu, 26 Sep 2024 17:32:04 +0300 Subject: [PATCH 3/3] docs(blog): update usecallback post (#6371) --- ...ck.md => 2024-09-26-react-use-callback.md} | 201 +++++++++++++++++- 1 file changed, 190 insertions(+), 11 deletions(-) rename documentation/blog/{2024-01-25-react-use-callback.md => 2024-09-26-react-use-callback.md} (65%) diff --git a/documentation/blog/2024-01-25-react-use-callback.md b/documentation/blog/2024-09-26-react-use-callback.md similarity index 65% rename from documentation/blog/2024-01-25-react-use-callback.md rename to documentation/blog/2024-09-26-react-use-callback.md index 7f30bf579a18..850369d78a85 100644 --- a/documentation/blog/2024-01-25-react-use-callback.md +++ b/documentation/blog/2024-09-26-react-use-callback.md @@ -4,11 +4,11 @@ description: Improve app performance with React useCallback() hook. slug: react-usecallback-guide authors: abdullah_numan tags: [react] -image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2022-09-20-react-use-callback/social.png +image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2022-09-20-react-use-callback/social-2.png hide_table_of_contents: false --- -**_This article was last updated on January 25, 2024 to expand code examples add more real use-cases for React useCallback hook._** +**_This article was last updated on January 25, 2024 to include comparisons between useCallback and useMeme and how to use useCallback with other hooks._** ## Introduction @@ -25,14 +25,13 @@ In this post, we explore how to use React `useCallback` in order to memoize a fu Steps we'll cover: +- [Introduction](#introduction) - [What is React `useCallback` ?](#what-is-react-usecallback-) - - [Why Use React `useCallback` Hook?](#why-use-react-usecallback-hook) -- [Memoize Functions with React `useCallback`: Ensuring A Callback's Referential Equality](#memoize-functions-with-react-usecallback-ensuring-a-callbacks-referential-equality) - - [Referential Inequality of Callbacks: Observing Unnecessary Re-renders](#referential-inequality-of-callbacks-observing-unnecessary-re-renders) - - [Memoizing an Event Listener with `useCallback()`](#memoizing-an-event-listener-with-usecallback) - - [React `useCallback` with Dependencies](#react-usecallback-with-dependencies) +- [useCallback vs useMemo](#usecallback-vs-usememo) - [When to Use React `useCallback`](#when-to-use-react-usecallback) - [When Not to Use React's `useCallback` Hook](#when-not-to-use-reacts-usecallback-hook) +- [Bonus:useCallback with Other Hooks](#bonususecallback-with-other-hooks) +- [Example](#example) ## What is React `useCallback` ? @@ -92,7 +91,7 @@ const UserPostsIndex = ({ signedIn }) => { }, []); return ( -
+

Your Posts

{signedIn ? `Signed in` : `Signed out `}

@@ -125,7 +124,7 @@ const UserPostsList = ({ userPosts, deletePost }) => { return (
{userPosts.map((post) => ( -
+