👨🏼‍💻

khriztianmoreno's Blog

Home Tags About |

Posts with tag react

Tech Stack 2025

2025-01-03
web-developmentjavascriptreact

The React ecosystem is a dynamic landscape of constantly evolving technologies. This article delves into a powerful technology stack for building full-stack applications in 2025, allowing you to bring your own product (such as a SaaS) or its Minimum Viable Product (MVP) to life.As an experienced Fullstack web developer, I've spent years honing my approach. Annual re-evaluations of the technology stack are crucial to balancing cutting-edge trends with long-term project stability and maintainability.My latest work experience, which culminated in November 2024, has provided me with valuable feedback. While I am satisfied with the initial technology stack, hindsight offers valuable lessons for future projects.Let's embark on this journey of discovery and explore the exciting possibilities that await in this concise yet powerful selection.React as a full-stack frameworkRemix.jsRemix.js is a full-stack web framework that prioritizes developer experience and leverages web fundamentals to create fast, resilient, and user-friendly web applications.Key Features:Data Loading and Fetching: Remix simplifies data fetching with built-in loaders and actions. Loaders fetch data before rendering a route, while actions handle form submissions and other side effects.File-System Routing: Routes are defined as files within your project's file system, making the routing structure intuitive and easy to understand.Server-Side Rendering (SSR): Remix excels at SSR, providing excellent SEO and faster initial page loads.Data Streaming: Remix can stream data to the client, improving perceived performance and user experience.Form Handling: Built-in form handling simplifies common tasks like validation, submission, and error handling.Testing: Remix encourages testing at various levels, including unit tests, integration tests, and end-to-end tests.Flexibility: Remix can be deployed to various environments, including Node.js servers and edge computing platforms like Cloudflare Workers.Benefits of Using Remix:Improved Performance: SSR and data streaming contribute to faster page loads and a smoother user experience.Enhanced Developer Experience: Remix's focus on developer experience makes it enjoyable to work with, thanks to its intuitive routing, data fetching mechanisms, and built-in features.Flexibility and Scalability: Remix can be deployed to various environments, making it adaptable to different project needs.Strong Community and Ecosystem: Remix has a growing community and a supportive ecosystem with various resources and tools available.In essence, Remix.js offers a modern and efficient approach to web development, empowering developers to build high-quality, performant, and user-centric applications.AstroConsidering a dedicated landing page? Astro shines for this task!While Remix.js excels at monolithic applications serving both static and dynamic content, Astro offers a compelling alternative specifically for crafting exceptional landing pages. Here's why Astro might be the perfect fit:Key Features:Lightning-Fast Performance: Astro prioritizes speed, delivering blazing-fast landing pages that keep visitors engaged.Intuitive File-Based Routing: Similar to Remix, Astro leverages a file-based routing system, making it easy to structure and manage your landing page content.Component-Based Development: Build reusable components for a streamlined development process and consistent design across your landing page.Integration with Multiple Frameworks: Astro seamlessly integrates with popular frameworks like React, Vue, and Svelte, empowering you to leverage your existing skills and preferences.Headless Content Management System (CMS) Support: Astro plays nicely with various headless CMS solutions, allowing for flexible content management of your landing page.Benefits of Using Astro for Landing Pages:Focus on Developer Experience: Astro's clean syntax and file-based structure streamline development, freeing you to concentrate on crafting an impactful landing page.Rapid Prototyping: Astro's speed makes it ideal for rapid prototyping and iterating on your landing page design.SEO Optimization: Astro generates clean, well-structured HTML, contributing to strong Search Engine Optimization (SEO) for your landing page.Reduced Build Times: Astro's incremental builds minimize build times, allowing for faster development cycles.By leveraging Astro's strengths, you can create a high-performing, developer-friendly landing page that captures leads and fuels your SaaS growth, all while saving valuable time to focus on core product development within your Remix/Next application.Server ComponentsImagine you're building a house. Server Components are like the construction workers handling the heavy lifting and specialized tasks. Instead of doing everything inside your house (the browser), these components work outside, on the server.What do they do?Fetch materials: They retrieve data from a database or API, like going to get bricks to build a wall.Do complex calculations: They perform complicated math or logic operations, like calculating the area of a room.Protect your house: They handle security tasks, like checking if someone is allowed to enter.Why are they useful?Your house is built faster: By doing part of the work on the server, your website loads faster for visitors.Your house is more secure: Sensitive data is handled in a safer place, out of reach of intruders.You can focus on the decoration: Server components handle the heavy lifting, so you can focus on making your website look nice and work well.Server FunctionsThink of your house having an intercom system. Server Functions are like using that intercom to ask a worker outside the house to do something.How do they work?You (your React component) tell the worker (the server function) what to do, like "please bring more bricks".The worker does the task and gives you the result.Why are they useful?It's very easy to communicate: You don't have to worry about the technical details of sending and receiving messages.You can do many things: You can ask the server function to do almost anything that a server component can do.Server ActionsImagine you have a list of pre-defined commands for your intercom. Server Actions are like those commands.What are they?They are server functions that are designed to perform specific tasks, like submitting a form or updating a database.Why are they useful?They are easy to use: They are already set up to do something specific, so you don't have to write a lot of code.There are many libraries that help you: There are libraries like next-safe-actions and zsa that provide you with pre-defined server actions for common tasks.State Management in ReactZustandZustand is a lightweight and flexible state management library for React applications. It offers a simple and intuitive API for managing global and local state, making it an excellent choice for projects of all sizes.Key Features:Minimalistic API: Zustand boasts a concise and easy-to-learn API with minimal boilerplate code.Performance-Oriented: Zustand is designed for optimal performance, with efficient state updates and minimal overhead.Flexible: It offers a flexible and modular approach to state management, allowing you to create and manage multiple stores as needed.Easy to Learn: The simple API and clear documentation make Zustand easy to learn and integrate into your React projects.import create from "zustand"; const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), })); function Counter() { const count = useStore((state) => state.count); const increment = useStore((state) => state.increment); return <button onClick={increment}>Count: {count}</button>; }RecoilRecoil.js is a state management library for React applications that provides a more granular and flexible approach to managing shared state compared to traditional methods like Context API. It offers a unique data flow graph that allows you to create complex state structures and derive new state from existing ones.Key Concepts:Atoms: These are the fundamental units of state in Recoil. Atoms are independent and can be subscribed to by multiple components. They provide a way to store and share simple values.Selectors: Selectors are pure functions that derive new state from existing atoms or other selectors. They allow you to create complex state structures and perform computations on the fly.RecoilRoot: This component is the root of your Recoil application. It provides the context for all Recoil atoms and selectors.Subscriptions: Components subscribe to atoms or selectors to receive updates when the state changes. Recoil uses efficient mechanisms to ensure that components only re-render when the data they depend on has actually changed.Advanced Features:Asynchronous Values: Recoil supports asynchronous values, allowing you to fetch data from APIs and manage loading states.Persistence: You can persist Recoil state to local storage or other storage mechanisms to restore it on subsequent page loads.Time Travel Debugging: Recoil provides tools for time travel debugging, allowing you to inspect and rewind state changes.Custom Hooks: You can create custom hooks to encapsulate complex state management logic.import { atom, selector, useRecoilState, useRecoilValue } from "recoil"; // Atom for user data (fetched asynchronously) const userDataAtom = atom({ key: "userData", default: null, effects_UNSTABLE: [ ({ setSelf }) => { // Fetch user data from API and set it }, ], }); // Selector to extract the user's name const userNameSelector = selector({ key: "userName", get: ({ get }) => { const userData = get(userDataAtom); return userData?.name || "Guest"; }, }); function UserProfile() { const userName = useRecoilValue(userNameSelector); return <div>Hello, {userName}!</div>; }CSS Styling in ReactTailwind CSSGame-Changer for Rapid DevelopmentWhile opinions on Tailwind CSS vary within the developer community, I strongly believe it's currently the most effective solution for rapid product development and long-term CSS maintainability.Based on my own experience and the feedback of many co-workers, Tailwind offers several key advantages:Rapid Prototyping Tailwind's utility-first approach empowers developers to quickly build and style UI elements without writing custom CSS classes. This significantly accelerates the prototyping and iteration process.Consistent Styling Tailwind provides a pre-defined set of utility classes, ensuring consistent styling across your entire project. This eliminates the need to constantly reinvent the wheel and helps maintain a cohesive design system.Improved Developer Experience Tailwind's intuitive syntax and autocompletion features in modern code editors enhance the developer experience, making it faster and more enjoyable to write and maintain CSS.Reduced CSS File Size By leveraging pre-defined utility classes, you can often significantly reduce the overall size of your CSS files, leading to faster page load times and improved performance.Strong Community and Ecosystem Tailwind boasts a large and active community, providing access to extensive documentation, helpful resources, and a wealth of community-built plugins and extensions.In my experience, the initial learning curve for Tailwind is relatively minor. Most developers become proficient within a week, and the long-term benefits in terms of development speed and maintainability far outweigh the initial investment.I encourage you to give Tailwind a try. You might just be surprised at how much it can streamline your CSS workflow and boost your productivity.Data Fetching in ReactReact/Tanstack QueryFor most data fetching needs, I prioritize Server Components due to their inherent performance advantages and improved data security. By handling data loading on the server, I can minimize the amount of JavaScript executed in the browser, resulting in faster initial page loads and a better user experience.However, for more complex scenarios such as infinite scrolling, pagination, or real-time data updates, I leverage the power of React Query. React Query provides a robust and flexible solution for managing data fetching, caching, and updates on the client-side.Example:Server Components: Ideal for fetching initial data for a product page, user profile, or blog post.React Query: Excellent for implementing infinite scrolling on a feed, managing paginated data in a table, or handling real-time updates for a chat application.By strategically combining Server Components and React Query, I can achieve an optimal balance between performance, maintainability, and developer experience in my React applications.Database & ORMPrismaMy Preferred Choice for Database InteractionsPrisma ORM remains my preferred choice for interacting with databases in my React projects. While newer ORMs like Drizzle are gaining traction, Prisma has proven to be a stable and reliable solution with a strong community and extensive documentation.Key Features of Prisma:Type Safety: Prisma generates TypeScript types from your database schema, ensuring type safety throughout your application and reducing the risk of runtime errors.Example:const user = await prisma.user.findUnique({ where: { id: 1 }, include: { posts: true }, });The generated types for user and posts provide clear guidance and prevent unexpected data structures.Declarative Schema Definition: Define your database schema using Prisma Schema Language, a declarative and intuitive syntax.Example:model User { id Int @id @default(autoincrement()) name String email String @unique posts Post[] } model Post { id Int @id @default(autoincrement()) title String content String author User @relation(fields: [authorId], references: [id]) authorId Int }Simplified Querying: Prisma provides a fluent and intuitive query builder API, making it easy to write complex database queries with minimal effort.Migrations: Prisma Migrate simplifies database schema changes with an easy-to-use migration system, allowing you to safely evolve your database over time.Benefits of Using PrismaIncreased Productivity: Prisma significantly improves developer productivity by automating repetitive tasks, such as generating SQL queries and managing database schema changes.Improved Code Quality: Type safety, generated types, and a focus on best practices contribute to higher code quality and fewer bugs.Enhanced Maintainability: Prisma's declarative approach and clear schema definitions make it easier to understand and maintain your database interactions over time.Strong Community and Ecosystem: Prisma has a large and active community, providing access to extensive documentation, tutorials, and support resources.While newer ORMs like Drizzle offer promising features, Prisma's stability, mature ecosystem, and strong focus on developer experience make it my go-to choice for most projects.SupabaseSupabase is an open-source Firebase alternative that offers a comprehensive suite of backend services, including a real-time PostgreSQL database, authentication, storage, and edge functions. It provides developers with a rapid and efficient way to build full-stack web applications without the hassle of managing infrastructure.Key Features of Supabase:Real-time PostgreSQL: Supabase leverages PostgreSQL for its robust database capabilities, allowing you to create complex data models and perform powerful queries. The real-time features enable you to build applications with live updates, such as chat apps and dashboards.Authentication: Supabase provides a flexible authentication system that supports various methods like email/password, social logins, and custom authentication providers. It also offers features like passwordless authentication and multi-factor authentication.Storage: Supabase includes a file storage service that allows you to upload and manage files directly from your application. You can generate public URLs for files and set permissions to control access.Edge Functions: These serverless functions allow you to execute custom code on the edge, closer to your users. This is useful for tasks like data transformation, server-side rendering, and custom authentication logic.GraphQL API: In addition to the REST API, Supabase also offers a GraphQL API, providing a more flexible and expressive way to query your data.Why Choose Supabase?Rapid Development: Supabase accelerates development by providing pre-built backend services, allowing you to focus on building your application's frontend.Scalability: Supabase is built on scalable infrastructure, making it suitable for applications of all sizes.Open Source: Being open-source, Supabase offers transparency, flexibility, and a strong community.Cost-Effective: Supabase offers a generous free tier and flexible pricing plans, making it affordable for both small and large projects.When to Use SupabaseReal-time Applications: Supabase is ideal for applications that require real-time updates, such as chat apps, collaborative tools, and dashboards.Rapid Prototyping: Supabase's ease of use makes it an excellent choice for quickly building prototypes and MVPs.Full-stack Web Applications: Supabase can be used as the backend for both simple and complex web applications.Data Handling & ValidationTypeScriptTypeScript is undeniably the industry standard for JavaScript projects. Its static type system, combined with modern features like interfaces and modules, offers a range of benefits, including stronger type safety, improved error detection, increased productivity, and a more enjoyable development experience. The industry's adoption of TypeScript is a testament to its value and effectiveness.ZodA Powerful Tool for Type-Safe ValidationZod has emerged as a leading choice for validation in React projects, particularly when combined with TypeScript. By leveraging Zod's type-safe approach, you can significantly enhance the robustness and maintainability of your applications.Key Features of ZodType-Safe Validation: Zod leverages TypeScript's type system to define and enforce data schemas. This ensures that the data received by your application conforms to the expected structure, preventing unexpected errors and improving data integrity.Declarative Schemas: Zod allows you to define data schemas declaratively using a concise and expressive syntax. This makes it easy to create complex validation rules for your data.Error Handling: Zod provides detailed and informative error messages, making it easier to identify and fix validation issues. These error messages can be easily integrated into your user interface to provide helpful feedback to users.Extensibility: Zod offers a flexible and extensible API, allowing you to create custom validation rules and integrate with other parts of your application.Benefits of Using ZodImproved Code Quality: By enforcing data types and validation rules, Zod helps you write more robust and reliable code with fewer unexpected errors.Enhanced Developer Experience: Zod's type-safe approach and informative error messages significantly improve the developer experience by making it easier to write, debug, and maintain your code.Improved User Experience: By providing clear and helpful error messages to users, Zod helps to improve the overall user experience of your application.Reduced Maintenance Costs: By catching data validation issues early on, Zod can help to reduce the long-term maintenance costs associated with your application.My ApproachWhile Zod offers powerful client-side validation capabilities, I prefer to primarily use it for server-side validation, particularly within Server Actions. This approach keeps client-side forms lightweight and avoids the complexity introduced by many third-party form libraries. By relying on native HTML validation for basic checks, I can maintain a lean and efficient form component architecture.Testing & ToolingMock Service Worker (MSW)One tool that has dramatically improved my workflow is Mock Service Worker (MSW). If you’re not using it yet, let me show you why it’s worth your attention.Mock Service Worker is a powerful JavaScript library for API mocking. It intercepts requests on the network level using Service Workers, allowing you to mock APIs directly in the browser or Node.js runtime. This makes it perfect for testing, debugging, and even development without relying on a backend.Why I Love Using MSWFor me, MSW solves so many problems that other mocking libraries can’t:Realistic Mocking: MSW intercepts requests at the network level, so the mocked behavior is almost indistinguishable from a real server. It’s like having a backend emulator in your pocket.Client and Server Testing: Whether you’re testing a React app or a Node.js service, MSW works seamlessly in both environments.Reduced Dependencies: No need for extra test servers or complex mocking setups. MSW keeps it clean and simple.Flexibility: You can mock REST, GraphQL, and even WebSocket APIs. If your app can make the request, MSW can mock it.Better Debugging: With clear logs and debugging tools, you know exactly which requests are being mocked and how.MSW vs. Traditional Mocking ToolsIn my experience, MSW stands out from tools like Axios interceptors or custom mocks:Scalability: With MSW, your mocks live outside your application logic, making them reusable and maintainable.Isolation: Unlike interceptors, MSW doesn’t interfere with your application’s code. This means no messy teardown code after tests.Browser-Like Behavior: By using Service Workers, MSW mimics browser-level behavior, ensuring your tests are as close to real-world conditions as possible.Why You Should Try MSWAPIs are the backbone of modern apps, and testing them doesn’t have to be painful. MSW provides a realistic, flexible, and developer-friendly way to mock APIs without unnecessary complexity.Whether you’re developing, debugging, or testing, MSW is a game-changer. It’s the tool I didn’t know I needed but now can’t live without.If you’re looking to elevate your development process in 2025, give MSW a try. Your team will thank you, and your code will shine.PlaywrightWhen it comes to modern web testing in 2025, Playwright has become one of my go-to tools. It’s not just a testing library; it feels like a power tool for front-end developers who want precision, speed, and versatility.Playwright is a Node.js library for browser automation. Built by Microsoft, it allows you to write end-to-end tests for web applications across all major browsers (Chromium, Firefox, WebKit) with one consistent API. It’s like having a Swiss Army knife for browser testing that’s sleek, powerful, and developer-friendly.Why Playwright Stands OutFrom my experience, Playwright excels in:Multi-Browser Support: Unlike Cypress, which only supports Chromium-based browsers out of the box, Playwright lets you test Chromium, Firefox, and WebKit. This makes it indispensable for ensuring your app works across different environments.Parallel Testing: Playwright’s built-in parallelization is a game-changer. Tests run faster, which keeps the CI pipeline smooth and developers productive.Headless and Headed Modes: Whether you’re debugging or running tests in CI, Playwright adapts seamlessly.Context Isolation: With Playwright, you can create isolated browser contexts that mimic different users. This is a lifesaver for apps with complex authentication flows or multi-tenant scenarios.API Testing: Playwright doesn’t stop at UI. You can make API calls directly within your test scripts, ensuring your front-end and back-end work harmoniously.Let’s See Some CodeHere’s a quick example of writing a Playwright test in TypeScript. This test checks a login page:import { test, expect } from "@playwright/test"; test.describe("Login Page Tests", () => { test("should log in successfully with valid credentials", async ({ page, }) => { await page.goto("https://example.com/login"); // Fill out the login form await page.fill("#username", "testuser"); await page.fill("#password", "securepassword"); await page.click('button[type="submit"]'); // Assert redirection to the dashboard await expect(page).toHaveURL("https://example.com/dashboard"); await expect(page.locator("h1")).toHaveText("Welcome, testuser!"); }); test("should show an error for invalid credentials", async ({ page }) => { await page.goto("https://example.com/login"); // Fill out the form with invalid data await page.fill("#username", "wronguser"); await page.fill("#password", "wrongpassword"); await page.click('button[type="submit"]'); // Assert error message is displayed await expect(page.locator(".error-message")).toHaveText( "Invalid credentials" ); }); });Testing is no longer optional in 2025. Users expect flawless experiences, and automation is how we deliver. Playwright combines power with developer-friendly features, making it a must-learn tool.If you haven’t explored it yet, now is the time. Your future self will thank you when your tests run faster, your bugs decrease, and your users stay happy.Deployment & HostingCloudflare (Domain & CDN)Cloudflare remains a cornerstone of modern web development. For me, it’s not just a service—it’s an integral part of building fast, secure, and scalable applications. Whether you’re an indie developer or part of a massive team, Cloudflare has tools that will elevate your stack.What is Cloudflare?Cloudflare is a comprehensive suite of web performance and security tools. It started as a Content Delivery Network (CDN), but today, it’s much more. With Cloudflare, you can optimize your website’s performance, protect it from malicious attacks, and even build serverless applications using their powerful edge computing platform.Why I Rely on CloudflareHere are the standout reasons why Cloudflare is an essential part of my stack:Speed Everywhere: With its global CDN, Cloudflare ensures that your app’s static assets are delivered lightning-fast, no matter where your users are. Their caching is a lifesaver for apps with heavy assets or global audiences.Unmatched Security: Cloudflare’s Web Application Firewall (WAF) and DDoS protection have saved me countless headaches. It’s like having a security team on autopilot.Serverless Edge Computing: Using Cloudflare Workers has been a game-changer. It allows me to run lightweight functions at the edge, reducing latency and offloading work from traditional servers.Ease of Use: Setting up Cloudflare takes minutes, but the benefits are immense. Their intuitive dashboard and developer-friendly tools make it easy to integrate with any stack.Cost-Effective: For the value it provides, Cloudflare’s pricing is unbeatable. Even their free tier is packed with features that can get you started without worrying about costs.Building modern apps means delivering fast, secure, and reliable experiences. Cloudflare empowers you to achieve all of this without overcomplicating your stack. From their unbeatable CDN to their innovative edge computing platform, it’s a tool I’d recommend to any developer looking to future-proof their applications in 2025.If you haven’t explored Cloudflare yet, now is the perfect time. Your users will feel the difference, and so will you.Other Utilities:React Email and ResendA Powerful Combination for Email DeliveryReact Email and Resend offer a compelling solution for crafting and delivering high-quality emails within your React applications.React Email: This library empowers you to build visually appealing and responsive email templates directly within your React components. By leveraging familiar React concepts like components, state, and props, you can create dynamic and maintainable email designs.Resend: This robust email API provides a reliable and efficient way to send transactional emails, such as welcome emails, password resets, and order confirmations. Resend offers features like high deliverability, robust analytics, and easy integration with your existing infrastructure.Key Benefits:Improved Developer Experience: React Email enhances the developer experience by allowing you to build email templates using familiar React patterns, leading to increased productivity and maintainability.Consistent Branding: By using React components for your email templates, you can ensure consistent branding and styling across your entire application, including emails.Enhanced Deliverability: Resend's robust infrastructure and focus on deliverability help ensure your emails reach the intended recipients reliably.Easy Integration: Resend offers a straightforward API and SDKs for easy integration with your React application.Example:// src/components/EmailTemplates/WelcomeEmail.jsx import * as React from "react"; import { Html, Head, Title, Body, P, H1 } from "@react-email/components"; export const WelcomeEmail = ({ name }) => ( <Html> <Head> <Title>Welcome to Our App!</Title> </Head> <Body> <H1>Hello, {name}!</H1> <P>Welcome to our app! We're excited to have you join us.</P> {/* ... more email content ... */} </Body> </Html> );// src/pages/api/sendWelcomeEmail.js import { Resend } from "resend"; const resend = new Resend(process.env.RESEND_API_KEY); export default async function handler(req, res) { try { await resend.emails.send({ from: "no-reply@yourdomain.com", to: "recipient@example.com", subject: "Welcome to Our App!", html: await renderToString(<WelcomeEmail name="John Doe" />), }); res.status(200).json({ success: true }); } catch (error) { console.error(error); res.status(500).json({ error: "Failed to send email" }); } }This example demonstrates how to use React Email to create a simple welcome email template and then use the Resend API to send it.By combining the power of React Email and Resend, you can streamline your email workflows, improve the quality of your email communications, and enhance the overall user experience.StripeStripe is a widely-used and robust payment gateway that offers a comprehensive suite of features for accepting online payments.Potential ChallengesComplexity: While powerful, Stripe's extensive feature set can sometimes feel overwhelming, especially for smaller projects or those with simpler payment requirements.Evolving API: Stripe continuously introduces new features and updates its API, which can occasionally require adjustments to your integration.import React, { useState } from "react"; import { loadStripe } from "@stripe/stripe-js"; const stripePromise = loadStripe("YOUR_STRIPE_PUBLIC_KEY"); function CheckoutForm() { const [loading, setLoading] = useState(false); const handleSubmit = async (event) => { event.preventDefault(); setLoading(true); try { const stripe = await stripePromise; const { error } = await stripe.redirectToCheckout({ lineItems: [ { price: "price_1234567890abc123", quantity: 1, }, ], }); if (error) { console.error(error); // Handle error (e.g., display an error message to the user) } } catch (error) { console.error(error); } finally { setLoading(false); } }; return ( <form onSubmit={handleSubmit}> {/* ... form fields ... */} <button type="submit" disabled={loading}> {loading ? "Processing..." : "Pay Now"} </button> </form> ); } export default CheckoutForm;Essentially, this is the tech stack that I would choose today for a new full-stack React project. This is just one possible combination, and the best tech stack for your project will ultimately depend on your specific requirements and priorities. I encourage you to explore these technologies and experiment with different combinations to find what works best for you.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno �

Fetching Data in React: A Beginner's Guide

2024-12-09
reactjavascriptweb-development

Imagine building a webpage that displays real-time weather data or a list of products from an online store. How does your React application get this information? The answer lies in data fetching. This process involves retrieving data from various sources (like APIs, databases) and incorporating it into your React components.Sequential Data Fetching:Think of sequential data fetching as a step-by-step process. You fetch one piece of data, wait for it to arrive, and then move on to the next.Example: Fetching user information, then their orders, and finally their address.// Sequential fetching using async/await async function fetchData() { const user = await fetchUser(); const orders = await fetchOrders(user.id); const address = await fetchAddress(user.id); // ... }Parallel Data Fetching:In parallel fetching, multiple data requests are made simultaneously.Example: Fetching user information, orders, and address at the same time.// Parallel fetching using Promise.all Promise.all([fetchUser(), fetchOrders(userId), fetchAddress(userId)]) .then(([user, orders, address]) => { // ... }) .catch((error) => { // ... });Prefetching Data:To enhance the perceived speed of your application, consider prefetching data before it's required. This technique is particularly effective for data that's likely to be needed soon but not immediately. For instance, when leveraging a framework like Next.js built on top of React, you can prefetch the subsequent page and its associated data using their Link component.Example: Fetching post details for the next page.<Link href="/posts/1" prefetch> <a>Post 1</a> </Link>As soon as this Link component becomes visible on the screen, the data for the "Next Page" is preloaded. These subtle optimizations can significantly improve the perceived performance of your app, making it feel more responsive.Conclusion:Choosing the right data fetching pattern depends on your specific use case.Sequential: Simple to implement, suitable for small-scale applications.Parallel: Improves performance for larger datasets, but can be more complex.Prefetching: Enhances user experience by reducing perceived loading times.Key Takeaways:Async/await: A modern way to handle asynchronous operations in JavaScript.Promises: A way to represent the eventual completion (or failure) of an asynchronous operation.Performance: Parallel fetching and prefetching can significantly improve performance.User experience: Prefetching can make your application feel snappier.Additional Tips:Error handling: Always handle errors gracefully to provide a better user experience.Caching: Store frequently accessed data to reduce the number of network requests.State management: Use libraries like Redux or Zustand to manage complex application state, especially when dealing with fetched data.By understanding these patterns, you can build more efficient and responsive React applications.Would you like me to elaborate on any of these concepts or provide more code examples?Profile@khriztianmoren

Why is it so difficult to pass state between client and server components?

2024-03-28
javascriptreactweb-development

The way we represent server components is different.Nothing like what we're used to so far.And because it's so different, it also changes where we handle state, how we handle it, and how we manage to sleep at night knowing that these are all important things we should know since last year, but most of us are completely unaware of.WHY?In fact, server components impact 3 of the most important parts of web development:PerformanceUser ExperienceThe way we, developers, write code and design our applications.It's not something we can ignore or take lightly.As you can see, I've been thinking about it a lot and you've probably been thinking about it too. Every developer who values ​​their keyboard is thinking about it.And there's this specific question... It's kind of a "chicken and the egg" type of question, mainly because they're both questions that get asked a lot.How ​​the heck do I handle state in server components if I don't have access to the state in the first place?Before I give you the answer, let me explain the problem at hand. Consider what happens when a server component requests a new render.Unlike client components where state is preserved between renders, server components don't have that luxury. Like a roguelike game, they always start from scratch.There is no inherent code or mechanism that can make a server component remember state. The backend has all those databases and design patterns and all those complex functions and for what? It can't even handle state.So what do we do? Do we simply use server components on completely static components that don't need any state? While this is a possible answer, it's also an approach that limits the effectiveness and tuning of server components in modern web applications. So, we're ruling it out.Because everything I said above has a drawback.While the backend may not handle client state like the client does, it does handle application state. So, in a way, we can handle state on the server. Just not in the way you think. And, actually, there's not one way.And, actually, there's not one way.There are THREE ways to handle state on the server.Which one we choose depends on what best suits our needs and current situation. And these 3 ways are:Prop drilling from the server to the componentscookiesstate hydrationNow, another million dollar question. Why can't we just handle all the state on the client?This is because server components advocate a separation of concerns. Which in simpler terms means that each part of the application should mind its own business. By decoupling state from rendering, we not only improve performance, but we also gain more control over the user experience.Profile@khriztianmoren

Will you still be using client-side components in 2024?

2024-02-14
javascriptreactweb-development

I love server components. But they're not for every occasion. In fact, using them at each and every opportunity you get is more like tenderizing a steak with the same hammer you used to hammer a pin.That said, there are also some cases where server components fit like Cinderella and her shoe.Dashboards & ReportsIn the case of dashboards and reports, you maximize performance by processing and rendering all the data on the server while still allowing the client to do what it was supposed to do in the first place.If we only use client components, we're offloading more tasks to it and making it perform more tasks than it can potentially handle.If you processed and rendered the data on the client, you'd spend a lot of time waiting around with an ugly spinner mocking you because of all the extra steps the client has to take to achieve the same result.First, you would need to get the data from your endpoint, which in turn would need to get data from the database, which in turn would need to get data from its logs.Not only that, but you also have to wait patiently for the data to arrive at the client, even if it arrives late.And only once it arrives, the client can start processing it and displaying it to the user.That's why, in this case, it's better to have the components locked and rendered directly on the server.And then, the client can get all the widgets its electronic heart desires, all streamed in parallel, which is a much faster and easier way to conduct business.Blog PostsBlog posts are some of the most static content you can find. After all, it's mostly just a bunch of words, in order, with the occasional image, gif, or meme here and there.With server components, you're pre-rendering the content on the server, which is the best case scenario in terms of SEO because it's delivered fast and complete.This use case above is what tends to throw developers off balance and confuse them. Because blog posts are the first thing they think of when it comes to SSR as well. So if they think of SSR and server components when they think of blog posts, it's natural to think that they're both the same thing or can be used interchangeably.Which is completely and utterly wrong and is a sin worthy of burning in hell for eternity, according to my previous blog post.But at the same time it makes sense from the point of view of results. Because although their approaches are very different in practice, the results are quite similar.Server-Side Data FetchingServer-side data fetching is a great way to give your code a security boost in case you otherwise reveal logic or API keys that are supposed to remain hidden or if you are paranoid about every piece of information you hold.You can use this type of data fetching to access not only your database but also any other API you want. All by being sneaky.However, there are cases where server-side components are NOT ideal and should in fact be avoided.High Interactivity RequirementsThis is fancy wording for anything you do something to and get something back. Kind of like Jell-O, but not quite.Something potentially more familiar and more in keeping with web development itself are things like forms and buttons.These components often have to react in some way to your actions, may have their own state, and communicate with other components on the client side, making them the weakest link when it comes to server components.Stateful ComponentsIf you take it as literally as possible and use the definition we've been using so far, there is no way to handle state in a server component.But if you squint, tilt your head a little to the side, defocus a bit, and take a deep breath, you can see that this is only half true.We'll learn what the nuances are another time, but for now let's just say that server components do NOT have access to state. After all, they don't have access to state-related hooks.ConclusionServer components are a powerful tool for specific scenarios in web development. Use them to improve performance and security for data-intensive tasks. But remember, they may not fit every situation, especially for interactive or stateful elements.Profile@khriztianmoreno see you soon

Server Components Vs Server-side Rendering

2024-01-28
javascriptreactweb-development

Did you know that server components and server-side rendering are two completely different things?Image descriptionAnd while they go hand-in-hand in many cases, there are equally many examples where you can and should use just one of them.Image descriptionNow, to be fair, given the online documentation most developers find about server components and server-side rendering, the fact that they assume these two things are the same isn't exactly a surprise.(This also gives them the perfect excuse to avoid server-side components altogether, rather than face the fact that this is something they should already be pretty good at using and will eventually have to learn anyway.)Image descriptionThat said, server-side components have a lot of advantages over server-side rendering, especially when it comes to building larger, more complex applications.Let's talk a bit more about the differences between the two.Server-Side RenderingServer-side rendering renders the page on the server. But obviously that's not really useful, so let's dig a little deeper.Server-side rendering renders the page on the server at request time, meaning that every time a client makes a request to the server — such as when a new visitor comes to your page — the server re-renders the same page and sends it to the client.And while this is great for SEO, since even the most dynamic pages appear static in the eyes of the robots that index them and use them for searches, server-side rendering has a lot of steps between the client making the request to the server and the page finally loading in front of you.First, when the request hits the server, the server statically renders the page and sends it back to the client, where the user gets a version of your page that's devoid of all interactivity.But that's not the only thing the server sends to the client. Along with the static page HTML, the server sends its condolences along with a big bundle of React code that the client will need to run to make the page dynamic again.But what happens when 90% of our page is static?You might think the correct answer is “practically nothing” and you wouldn’t be exactly wrong, but just like in Calculus class, you lose points because this isn’t the answer I was looking for.In reality, there’s still a lot of JavaScript being sent to the client and it needs to be executed without changing much on the page itself.This is a waste of time and data and is one of the main reasons server components were created.(Which also means that if you’re using SSR as an alternative to server components, you’ve hit a big snag and it’s time to change your ways before you get thrown into a “slow performance” jail.)Server ComponentsServer components, like SSR, are rendered on the server, but they have the unique ability to include server-side logic without sending any additional JavaScript to the client, since server components are executed on the server.This means that while in SSR the JavaScript code is executed on the client, in Server Components, the code is executed directly on the server. And the client only receives the output of the code through the server payload, like a baby getting its food chewed up.So, in a nutshell, the difference between server components and server-side rendering is all about when and how the code is executed. In the case of server components, there is no need to send or handle any additional code on the client side because we already run it on the server.The only code needed is if you are tying the server and client components together and the client has to tie the two together.The big benefit of server components is that they offer higher performance because they need less client-side JavaScript and offload all the processing and data retrieval to the server while the client can relax and drink Aperol Spritz until the rendered HTML arrives.At that point, the client simply displays it to the end user and takes all the credit for the hard work done by the server.And while this may seem a bit complex right now, it will all become clearer as you learn more about server components. Which is something that can't really be avoided, as they are becoming more and more popular in use cases like:Having a lot of heavy calculations that need a lot of processingPrivate API access (to keep things secret… secret)When most of the client side is already static and it would be a waste to send more JavaScript to the clientConclusionWhile server components may seem daunting right now, they are mostly something new and strange. Once you get to know them better and learn some of the basics, you'll realize that they are not as complex as they seem.The learning curve is not as steep as you might think and just because they have "sever" in the name, it doesn't mean they have to be something strange and cryptic that we frontend developers should stay away from. In fact, they are quite similar to client components and even more lightweight as they lack things like state hooks, etc.Profile@khriztianmoren

Hacks for effective Fullstack development with React and Node

2023-04-17
javascriptnodejsreact

Today I'm going to show you an optimal workflow for effective development with Node.js and React. If you've ever worked on a project with multiple package.json files, you might know the pain of juggling multiple terminal tabs, remembering which commands start which server, or handling CORS errors.Fortunately, there are some tools available that can alleviate some of these headaches.FullstackMonorepo Setup for React and NodeLet's say we're working on a monorepo with two package.json files: one is in a client directory for a React front-end powered by Create React App, and one is at the root of the repository for a Node back-end that exposes an API that our React app uses. Our React app runs on localhost:3000 and our Node app runs on localhost:8080. Both apps are started with npm startSince we have two package.json files, this means that in order to get our front-end and back-end up and running, we need to make sure we've run npm install and npm start in both the root directory and the client directory. Here's how we simplify this.1. Running two servers at the same timeOne improvement we can make to our development workflow is to add a build tool to run multiple npm commands at the same time to save us the trouble of running npm start in multiple terminal tabs. To do this, we can add an npm package called concurrently to the root of our project.In the root of our project, we will install it as a development dependency.npm install -D concurrentlyThen, in our root package.json scripts, we will update our start script to use them simultaneously.{ "name": "my-app", "version": "1.0.0", "main": "index.js", "scripts": { "start": "concurrently --kill-others-on-fail npm run server npm run client", "server": "node index.js", "client": "cd client && npm start" }, "dependencies": { "express": "^4.17.1" }, "devDependencies": { "concurrently": "^6.0.1" } }Now, we have three npm scripts:npm run server starts our Node application,npm run client runs npm start in the client directory to start our React application,npm start runs npm run server and npm run client at the same time.2. Installing front-end and back-end dependencies with a single commandAnother aspect of our workflow that we can improve is the installation of dependencies. Currently, we need to manually run npm install for every package.json file we have when setting up the project. Instead of going through that hassle, we can add a postinstall script to our root package.json to automatically run npm install in the client directory after the installation has finished in the root directory.{ "name": "my-app", "scripts": { ..., "postinstall": "cd client && npm install" }, }Now, when we install our monorepo, all we need to do to get it up and running is run npm install and then npm start at the root of the project. There is no need to enter any other directory to run other commands.3. Proxy API requests from the backendAs I mentioned earlier, our Node backend exposes the API endpoints that our React app will use. Let's say our Node app has a /refresh_token endpoint.Out of the box, if we tried to send a GET request to http://localhost:8080/refresh_token from our React app at http://localhost:3000, we would run into CORS issues. CORS stands for cross-origin resource sharing.Typically when you encounter CORS errors, it's because you're trying to access resources from another domain (i.e., http://localhost:3000 and http://localhost:8080), and the domain you're requesting resources from is not allowed.To tell the development server to proxy any unknown requests to our API server in development, we can set up a proxy in our React app's package.json file. In client/package.json, we'll add a proxy for http://localhost:8080 (where our Node app is running).{ "name": "client-app", "proxy": "http://localhost:8080", "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, ... }Now, if we restart the server and set a request to our Node app's /refresh_token endPoint (without http://localhost:8080) using fetch(), the CORS error should be resolved.fetch("/refresh_token") .then((res) => res.json()) .then((data) => console.log(data)) .catch((err) => console.error(err));Next time you're working on a monorepo project like this, try these three tips to streamline your development workflow.That's all folks! I hope this helps you become a better dev!Profile@khriztianmoren

React useEffect

2022-09-05
javascriptreacthooks

useEffect is probably the most confusing and misunderstood hook in React. Today I want to clarify that for you.We use hooks all the time at Make It Real and understanding useEffect is crucial if we are going to write modern-style React code.Next, we will see:What is useEffect?How to run an effect on every renderHow to run an effect only on the first renderHow to run an effect on the first render and re-run it when a "dependency" changesHow to run an effect with cleanupWhat is useEffect?The useEffect hook allows us to perform side effects in our function components. Side effects are essentially anything where we want an "imperative" action to happen. This includes things like:API callsUpdating the DOMSubscribing to event listenersAll of these are side effects that we might need a component to perform at different times.Running useEffect on every renderThe useEffect hook does not return any value, but it takes two arguments. The first is mandatory and the second is optional. The first argument is the callback function of the effect we want the Hook to execute (i.e., the effect itself). Suppose we wanted to place a console.log() message inside the useEffect callback.import { useEffect } from "react"; export const FunctionComponent = () => { useEffect(() => { console.log("run for every component render"); }); return ( // ... ); }By default, the effect set in the useEffect hook runs when the component renders for the first time and after every update. If we run the above code, we will notice that the console.log('run for every component render') message is generated as our component renders. If our component ever re-renders (for example, from a state change with something like useState), the effect would run again.Sometimes, re-running an effect on every render is exactly what you want. But most of the time, you only want to run the effect in certain situations, such as on the first render.How to run useEffect only on the first renderThe second argument of the useEffect hook is optional and is a dependency list that allows us to tell React to skip applying the effect until certain conditions are met. In other words, the second argument of the useEffect hook allows us to limit when the effect will run. If we simply place an empty array as the second argument, this is how we tell React to only run the effect on the initial render.import { useEffect } from "react"; export const FunctionComponent = () => { useEffect(() => { console.log("run only for first component render (i.e., component mount)"); }, []); return ( // ... ); }With the above code, the console.log() message will only trigger when the component mounts for the first time and will not re-trigger, even if the component re-renders multiple times.This is much more "efficient" than running on every render, but isn't there a happy medium? What if we want to re-run the effect if something changes?Running useEffect on the first render and re-running it when the dependency changesInstead of making an effect run once at the beginning and on every update, we can try to restrict the effect to run only at the beginning and when a certain dependency changes.Suppose we wanted to trigger a console.log() message every time the value of a state property changes. We can achieve this by placing the state property as a dependency of the effect callback. See the following code example:import { useState, useEffect } from "react"; export const FunctionComponent = () => { const [count, setCount] = useState(0); useEffect(() => { console.log( "run for first component render and re-run when 'count' changes" ); }, [count]); return ( <button onClick={() => setCount(count + 1)}> Click to increment count and trigger effect </button> ); };Above, we have a button in the component template responsible for changing the value of the count state property when clicked. Each time the count state property changes (i.e., each time the button is clicked), we will notice that the effect callback runs and the console.log() message triggers.Running an effect with cleanupAn effect callback runs every time on the initial render and when we specify when an effect should run. The useEffect hook also provides the ability to run a cleanup after the effect. This can be done by specifying a return function at the end of our effect.import { useState, useEffect } from "react"; export const FunctionComponent = () => { const [count, setCount] = useState(0); useEffect(() => { console.log( "run for first component render and re-run when 'count' changes" ); return () => { console.log("run before the next effect and when component unmounts"); }; }, [count]); return ( <button onClick={() => setCount(count + 1)}> Click to increment count and trigger effect </button> ); };In the above example, we will notice that the cleanup function message triggers before the desired effect runs. Additionally, if our component ever unmounts, the cleanup function will also run.A good example of when we might need a cleanup is when we set up a subscription in our effect but want to remove the subscription whenever the next subscription call is to be made, to avoid memory leaks.These are mainly all the different ways the useEffect hook can be used to run side effects in components. I invite you to check out this visual guide to useEffect by ALEX SIDORENKO that illustrates these concepts through a series of GIFs that are both clever and effective, especially for visual learners. There is also a visualization of how first-class functions work if you want more.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno ďż˝

Why Storybook? The component development tool used by over 30,000 projects

2022-08-22
javascriptstorybookreact

Storybook is a tool for developing components and user interfaces faster than ever. Storybook is incredibly versatile: you can use it with a variety of JavaScript libraries and frameworks, not just React. It is available for Vue, React, Svelte, Angular, and Ember.js.StorybookIf you have been developing your components the old-fashioned way, in your text editor or IDE, a tool like Storybook allows you to unlock greater productivity when developing components. Next, you will learn what Storybook is, how it works, and if it is suitable for your team.The problems of developing components traditionallyLet's start by looking at the friction involved with the typical component development process:You receive a task to develop a feature: let's say it's a form on the checkout page.Then, you need to set up the development environment: connect to the VPN, run the backend, run the frontend, etc.Finally, you get to the page where the feature will live.It is cumbersome to navigate between multiple pages, fill out forms, and click buttons every time you need to get to where the feature should be. Sometimes, your components have multiple states, for example, loading, success, and error. It is not always easy to replicate all the states of a component, which leads you to modify the component code just to force a specific state.Storybook isolates your components: easier component debuggingYou may have gone through these situations and encountered the pain involved in this type of component development workflow.Most of the time, while developing, you want to focus on the component you are creating, so other elements on a page become noise. Having a way to quickly access any component or feature, and also being able to simulate all use cases, is incredibly beneficial and saves you a lot of time.Storybook provides you with this type of component isolation so you can work only on the component you have in mind, without having to interact with other components.What is Storybook?Storybook is an open-source tool that helps you develop user interface components in isolation. It runs on your codebase, but separately from your application, so it works like a sandbox, allowing developers not to be distracted by incomplete APIs, unstable data, and other external dependencies. It integrates with frameworks like React, Vue, Svelte, Angular, and others.Think of Storybook as a real book, with an index of pages that links to the user interface components. Each component has stories to tell about itself, and these stories represent the different states of the component's user interface. Regardless of the situation, even if you are offline, you will be able to access that page and easily find and play with the components.Due to its productivity and collaboration advantages, Storybook is used by more than 30,000 open-source projects, especially component libraries. However, many tech companies, such as Airbnb, Atlassian, and JetBrains, are among its users.Who is Storybook for?Some people seem to think that Storybook is a tool only for component library developers, and that is certainly not the case.Storybook helps us build from the simplest and most atomic component, like a button or an input, to complex features or entire pages.Since Storybook helps us summarize the user interface of applications, designers and QA can benefit from it. With Storybook, you can facilitate the development of a design system and share a single language with designers. QA can get an overview and test functionalities in isolation. Storybook can even be used to demonstrate functionality to stakeholders, as if it were a demo.Many companies have made their Storybooks public. They are not only an inspiration but also a learning guide for teams new to Storybook, and you can find a list of public Storybooks here.How it worksFrom a technical aspect, Storybook is essentially a React application that runs on your codebase, separately from your main application. You start it by running a CLI command. It will look for files in your codebase that contain a .stories.* extension, gather all those components, and display them in a nice user interface.Suppose you are creating, for example, a restaurant card. You would have a RestaurantCard.stories.tsx file, which represents the component with mocked properties for each scenario.It is important to note that Storybook does not produce any production code. Your .stories.tsx files are used solely for development purposes.I hope this was helpful and/or taught you something new!Profile@khriztianmoren

Redux explained in a simple and succinct way for React developers

2022-08-10
reactreduxjavascript

Redux is a widely used state management library for React and TypeScript applications. It’s easier than ever to manage state in React thanks to the useState React Hook, as well as the Context API. However, when your codebase grows very large, you’ll need a more powerful and structured state management solution, rather than an ad-hoc one. That’s where Redux can help.ReduxWhy do you need Redux?When working with React, you usually end up with state that is used globally throughout the entire application.One of the approaches to sharing state across the component tree is to use the Context API. We often use it in combination with hooks like useReducer and useState to manage global application state.This approach works, but it can only take you so far. In the end, you have to invent your own ways to manage side-effects, debug, and split state management code into modules so that it doesn't become an incomprehensible mess.A better idea is to use specialized tools. One such tool to manage global application state is Redux.How Redux WorksRedux is a state management framework that is based on the idea of ​​representing the global state of the application as a reducer function.In Redux, to manage state, we define a function that accepts two arguments: state, for the previous state, and action, the object that describes the state update.function reducer(state = "", action: Action) { switch (action.type) { case "SET_VALUE": return action.payload; default: return state; } }This reducer represents a string value. It handles only one type of action: SET_VALUE.If the received action field type is not SET_VALUE, the reducer returns the unchanged state.After having the reducer, we create the store using the redux createStore method.const store = createStore(reducer, "Initial Value");The store provides a subscription method that allows us to subscribe to updates to the store.store.subscribe(() => { const state = store.getState(); console.log(state); });Here, we've passed a callback that logs the state value to the console.To update the state, we dispatch an action:store.dispatch({ type: "SET_VALUE", payload: "New value", });Here we pass an object representing the action (action). Each action is required to have the type field and optionally, payload.Usually, instead of creating actions in place, people define action creator functions:const setValue = (value) => ({ type: "SET_VALUE", payload: value, });And this is the essence of Redux.Why can't we use the useReducer hook instead of Redux?Since version 16.8, React supports Hooks. One of them, useReducer, works very similarly to Redux.It's easy to manage application state using a combination of useReducer and the React Context API.So why do we need Redux if we have a native tool that also allows us to represent state as a reducer? If we make it available throughout the application using the Context API, won't that be enough?Redux offers some important advantages:Browser Tools: You can use Redux DevTools to debug your Redux code. It allows us to see the list of dispatched actions, inspect the state, and even travel back in time. You can toggle through the history of actions and see how the state dealt with each of them.Handling Side Effects: With useReducer, you have to invent your own ways to organize the code that makes network requests. Redux provides the Middleware API to handle that. Additionally, there are tools like Redux Thunk that make this task even easier.Testing: Since Redux is based on pure functions, it is easy to test. All testing comes down to checking the output against the given inputs.Patterns and code organization: Redux is well studied and there are recipes and best practices you can apply. There is a methodology called Ducks that you can use to organize Redux code.Building with ReduxNow that you've seen examples of what Redux does and how it works, you're ready to use it in a real project.Profile@khriztianmoren

A better way to build React component libraries

2022-07-05
reactjavascriptweb-development

Today we'll quickly go over four programming patterns that apply to shared components in React.ReactUsing these allows you to create a well-structured shared component library. The benefit you get is that developers in your organization can easily reuse components across numerous projects. You and your team will be more efficient.Common PatternsIn this post, I show you four API patterns that you can use with all your shared components. These are:JSX children pass-throughReact fowardRef APIJSX prop-spreading cont TypeScriptOpinionated prop defaultsPattern 1: JSX Children Pass-ThroughReact provides the ability to compose elements using the children prop. The shared component design leans heavily on this concept.Allowing consumers to provide the children whenever possible makes it easier for them to provide custom content and other components. It also helps align component APIs with those of native elements.Let's say we have a Button component to start with. Now we allow our Button component to render its children, like this:// File: src/Button.tsx export const Button: React.FC = ({ children }) => { return <button>{children}</button>; };The definition of React.FC already includes children as a valid prop. We pass it directly to the native button element.Here is an example using Storybook to provide content to the Button.// File: src/stories/Button.stories.tsx const Template: Story = (args) => ( <Button {...args}>my button component</Button> );Pattern 2: forwardRef APIMany components have a one-to-one mapping to an HTML element. To allow consumers to access that underlying element, we provide a referencing prop using the React.forwardRef() API.It is not necessary to provide a net for day-to-day React development, but it is useful within shared component libraries. It allows for advanced functionality, such as positioning a tooltip relative to our Button with a positioning library.Our Button component provides a single HTMLButtonElement (button). We provide a reference to it with forwardRef().// File: src/buttons/Button.tsx export const Button = React.forwardRef < HTMLButtonElement > (({ children }, ref) => { return <button ref={ref}>{children}</button>; }); Button.displayName = "Button";To help TypeScript consumers understand what element is returned from the ref object, we provide a type variable that represents the element we are passing it to, HTMLButtonElement in this case.Pattern 3: JSX Prop-SpreadingAnother pattern that increases component flexibility is prop propagation. Prop propagation allows consumers to treat our shared components as drop-in replacements for their native counterparts during development.Prop propagation helps with the following scenarios:Providing accessible props for certain content.Adding custom data attributes for automated testingUsing a native event that is not defined in our props.Without prop propagation, each of the above scenarios would require explicit attributes to be defined. prop propagation helps ensure that our shared components remain as flexible as the native elements they use internally.Let's add prop propagation to our Button component.// File: src/buttons/Button.tsx export const Button = React.forwardRef< HTMLButtonElement, React .ComponentPropsWithoutRef<'button'> >(({ children, ...props }, ref) => { return ( <button ref={ref} {...props}> {children} </button> ); });We can reference our remaining props with the spread syntax and apply them to the button. React.ComponentPropsWithoutRef is a type utility that helps document valid props for a button element for our TypeScript consumers.Some examples of this type checking in action:// Pass - e is typed as // `React.MouseEventMouseEvent>` <Button onClick={(e) => { console.log(e) }} /> // Pass - aria-label is typed // as `string | undefined` <Button aria-label="My button" /> // Fail - type "input" is not // assignable to `"button" | // "submit" | "reset" | undefined` <Button type="input" />Pattern 4: Opinionated DefaultsFor certain components, you may want to map default attributes to specific values. Whether to reduce bugs or improve the developer experience, providing a set of default values ​​is specific to an organization or team. If you find the need to default certain props, you should ensure that it is still possible for consumers to override those values ​​if necessary.A common complexity encountered with button elements is the default value type, "submit". This default type often accidentally submits surrounding forms and leads to difficult debugging scenarios. Here's how we set the "button" attribute by default.Let's update the Button component to return a button with the updated type.// File: src/buttons/Button.tsx return ( <button ref={ref} type="button" {...props}> {children} </button> );By placing the default props before the prop broadcast, we ensure that any value provided by consumers is prioritized.Look at some open source librariesIf you're building a component library for your team, take a look at the most popular open source component libraries to see how they use the patterns above. Here's a list of some of the top open source React component libraries to look into:Ant DesignRainbow UIGrommetProfile@khriztianmorenoUntil next time

Systems Design with ReactJS and Storybook

2020-03-05
javascriptreactstorybookdesign-system

Document and test your React components in isolation using Storybook.Storybook ReactStorybook Reacttl;dr: In this post, we will learn how to set up the necessary infrastructure to build a reusable component design system in React, using Storybook.Let's start by understanding that a design system is a series of components that can be reused in different combinations. Design systems allow you to manage design. If we go to designsystemsrepo.com, we can see the design systems used by some of the largest companies and strongest brands, such as Priceline, Apple, IBM, WeWork, GitHub, and even the US government.Design systems can be a significant productivity multiplier in any medium to large-sized project or company, as we can document our components as we develop them, ensuring a consistent look and feel across all screens, and having a continuous workflow between designers and developers.<iframe width="100%" height="315" src="https://www.youtube.com/embed/guteTaeLoys?si=_8X6KwUOZCjQvwan" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>Throughout this video, we will progressively build a very simple design system that contains a single button, but I will show several of the features that Storybook can offer to improve our development experience and project speed.We will learn to set up the StoryBook used in production by everyone from Lonely Planet to Uber, but at the same time, we will keep it as simple as possible, so we can reuse these APIs for our future needs.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno ďż˝

What is a “side effect”?

2020-02-20
javascriptreactredux

In the previous post, we learned a bit about immutability and why it should matter when writing our code, especially our reducers.This time, I want to talk a bit about side effects and how working with pure functions can help us. However, first, let's see what makes a function pure and why it is closely related to immutability.Immutability RulesTo be pure, a function must follow these rules:A pure function must always return the same value when given the same inputs.A pure function must not have any side effects.“Side effects” is a broad term, but it basically means modifying things outside the immediate scope of that function. Some examples of side effects are:Mutating/modifying input parameters, like giveAwesomePowers (function from the previous post)Modifying any other state outside the function, such as global variables, or document.(anything) or window.(anything)Making API callsconsole.log()Math.random()API calls might surprise you. After all, making a call to something like fetch('/users') might not change anything in your UI.But ask yourself this: if you called fetch('/users'), could it change anything anywhere? Even outside your UI?Yes. It will create an entry in the browser's network log. It will create (and perhaps later close) a network connection to the server. And once that call reaches the server, all bets are off. The server could do whatever it wants, including calling other services and making more mutations. At the very least, it will probably place an entry in a log file somewhere (which is a mutation).So, as I said: “side effect” is a pretty broad term. Here is a function that has no side effects:You can call this function once, you can call it a million times and nothing will change. I mean, technically, this satisfies Rule 2. Calling this function will not directly cause any side effects.Also, every time you call this function like add(1, 2) you will get the same answer. No matter how many times you call add(1, 2) you will get the same answer. That satisfies Rule 1: same inputs == same outputs.JS Array Methods That MutateCertain array methods will mutate the array they are used on:push (add an item to the end)pop (remove an item from the end)shift (remove an item from the beginning)unshift (add an item to the beginning)sortreversesplicePure Functions Can Only Call Other Pure FunctionsA possible source of trouble is calling an impure function from a pure one.Purity is transitive and it's all or nothing. You can write a perfectly pure function, but if you end it with a call to some other function that eventually calls setState or dispatch or causes some other kind of side effect… then all bets are off.Now, there are some types of side effects that are “acceptable”. Logging messages with console.log is fine. Yes, technically it is a side effect, but it is not going to affect anything.A Pure Version of giveAwesomePowersNow we can rewrite our function keeping the Rules in mind.giveAwesomePowers — Pure functiongiveAwesomePowers — Pure functionThis is a bit different now. Instead of modifying the person, we are creating a completely new person.If you haven't seen Object.assign, what it does is assign properties from one object to another. You can pass it a series of objects, and it will combine them, from left to right, while overwriting any duplicate properties. (And by “from left to right”, I mean that running Object.assign(result, a, b, c) will copy a into result, then b, then c).However, it does not do a deep merge: only the immediate properties of each argument will be moved. Also, most importantly, it does not create copies or clones of the properties. It assigns them as they are, keeping the references intact.So the above code creates an empty object, then assigns all the properties of person to that empty object and then assigns the specialPower property to that object as well. Another way to write this is with the object spread operator (spread):giveAwesomePowers — ES6 || spreadgiveAwesomePowers — ES6 || spreadYou can read this as: “Create a new object, then insert the properties of person, then add another property called specialPower”. As of this writing, this spread syntax is officially part of the JavaScript specification in ES2018.Pure Functions Return Brand New ObjectsNow we can rerun our experiment from before, using our new pure version of giveAwesomePowers.The big difference is that person was not modified. Mafe has not changed. The function created a clone of Mafe, with all the same properties, plus the ability to become invisible.This is kind of a weird thing about functional programming. Objects are constantly being created and destroyed. We did not change Mafe; we created a clone, modified her clone, and then replaced Mafe with her clone.I hope this has been helpful and/or taught you something new!Profile@khriztianmoreno �

What is immutability?

2020-02-10
javascriptreduxreact

Immutability in React and ReduxImmutability can be a confusing topic, and it appears everywhere in React, Redux, and JavaScript in general.You may have encountered errors where your React components do not re-render, even though you know you have changed the props, and someone says, "You should be making immutable state updates." Maybe you or one of your teammates regularly writes reducers in Redux that mutate the state, and we have to constantly correct them (the reducers, or our teammates 😄).It's complicated. It can be very subtle, especially if you're not sure what to look for. And honestly, if you're not sure why it's important, it's hard to care.

Introduction to Apollo Client with React for GraphQL

2020-01-30
javascriptreactgraphqltutorial

GraphQL has become popular recently and is likely to replace the Rest API. In this tutorial, we will use the Apollo Client to communicate with GitHub's GraphQL API. We will integrate Apollo Client with ReactJS, but you can also use it with other platforms (VueJS, Angular, etc).

Flux Standard Action (FSA)

2020-01-20
reactjavascriptreduxtutorial

It is a lightweight specification that defines the structure of an action, to be implemented in libraries that use the Flux pattern or architecture.Compliance with FSA helps developers create abstractions that can work with different implementations of Flux.Flux Standard Action — ExampleFlux Standard Action — ExampleIt all started after Facebook published its architecture/pattern Flux, many libraries implemented the Flux philosophy, Redux was one of them.Flux can be divided into several concepts Dispatcher, Store, Action, and View. But in this post, we are going to learn about the Action part and how to work with them in a more standardized way, so later we can use other libraries that implement the FSA philosophy.Before delving deeper into today's main topic, let's get to know the concept of Action and how it is defined by flux:Actions define the internal API of your application. They capture the ways to interact with your application. They are simple objects that consist of a “type” field and data.The specification would lead to the following object:{ type: 'ADD_TODO', text: 'TODO content' }The only problem with this simple example is that the developer can choose any property name for the values. All the following names are valid: title, name, text, todoName, etc. It is impossible to know what properties to expect from ADD_TODO in the Redux reducer.It would be much easier to work with Flux actions if we could make certain assumptions about their shape. Maybe defining a minimum common standard for these patterns would allow us to have the necessary abstraction to communicate our actions with the reducer. This is something that Flux Standard Action (FSA) comes to solve.To go into a bit more detail about FSA, it is necessary to start from the following premise that Flux Standard Action provides us about actions:An action MUST:be a plain JavaScript object.have a type property.An action MAYhave an error property.have a payload property.have a meta property.An action MUST NOT include properties other than type, payload, error, and meta.But then what does each of these properties that our JavaScript object can contain mean?Let's see each of thesetypeThe required property type identifies the nature of the action that has occurred to the consumer, type is a constant of type StringpayloadThe optional payload property MAY be any type of value. It represents the action's payload. Any information about the action that is not the type or the status of the action should be part of the payload field.By convention, the payload SHOULD be an object.errorThe optional error property MAY be set to true if the action represents an error.An action whose error is true is analogous to a rejected Promise. By convention, the payload SHOULD be an error object.If the error has any value other than true, including undefined and null, the action MUST NOT be interpreted as an error.metaThe optional meta property MAY be any type of value. It is intended for any additional information that is not part of the payload.The Flux Standard Action (FSA) concept is used by some libraries that can help us reduce the repetitive text we have to create for our actions.Librariesredux-actions — a set of helpers to create and handle FSA actions in Redux.redux-promise — A middleware that supports FSA actions.redux-rx — RxJS utilities for Redux, including a middleware that supports FSA actions.I hope to have the opportunity to give an introduction on how to reduce Redux boilerplate with Redux-Actions in a future occasion.I hope this has been useful and/or taught you something new!Profile@khriztianmoreno �