React.js Quick Refresher Guide

Posted by: Suprotim Agarwal , on 7/20/2023, in Category Reactjs
Views: 10703
Abstract: This guide provides a comprehensive overview of React.js, from creating components and passing props to using popular libraries like Redux and React Router. It covers advanced topics such as React Hooks, Context, and Higher-Order Components, and provides tutorials on styling components conditionally with CSS, using Formik and Yup for form validation, and testing components with Jest and Enzyme. Perfect for beginners and experienced React developers alike.

React.js is a popular JavaScript library used for building dynamic user interfaces.

With React.js, developers can build reusable UI components and manage the state of an application using components and props. It utilizes a virtual DOM, enabling efficient rendering and updates of components.

React.js also provides several features to handle events, work with forms, and manage the lifecycle of an application. Moreover, React.js is often used with other popular libraries such as Redux and React Router to create more complex applications.

The topics covered in this guide are aimed at individuals who already have had some experience in the past with building React applications and are looking for a refresher guide to refresh their knowledge of React.

I am absolutely new to JavaScript and React.js!!

If you are absolutely new to React, then I suggest you first go through our upcoming Getting Started with JavaScript: Beginner to Advanced(Multi Part Series) and then Getting Started with React: Beginner to Advanced(Multi Part Series) guides to be published soon in April 2023. Subscribe to these guides over here and we’ll email you the chapters as they get ready.

To run the code shown in this guide, you will need an editor like VS Community Edition , node, npm and npx. Check this prerequisite guide here.

React.js quick start

What does this guide cover?

This guide covers everything you need to know to refresh your knowledge of the React.js JavaScript library. Starting with the basics of creating components and passing props, you’ll learn how to manage component state, handle events, and render components conditionally. You’ll also learn how to create forms and validate user input using Formik and Yup, and style your components conditionally with CSS.

In addition to core React concepts, this guide covers some advanced topics like React Hooks, Context, and Higher-Order Component0s, as well as popular libraries and frameworks like Redux, React Router, and Material-UI. You’ll also learn how to use Axios to fetch data from APIs and how to test your React components using Jest and Enzyme.

Whether you’re a beginner or an experienced React developer, this guide has something for everyone. By the end of this guide, you’ll have a quick refresher of React.js library.

1. Creating Components:

import React from 'react';

class SomeComponent extends React.Component {
    render() {
        return <div>Hello World!</div>;
    }
}

Shown above is a basic example of a React component written in JavaScript using the React library.

The component is called “SomeComponent” and extends the “React.Component” class. It defines a “render” method that returns a simple JSX expression, which in this case is a ‘div’ element with the text “Hello World!”.

To use this component in a React application, it needs to be exported from this module and then imported into another file where it can be rendered as part of the application’s UI. For example:

import React from 'react';
import ReactDOM from 'react-dom';
import SomeComponent from './MyComponent';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    {/* The 'msg' is props that will be added
      in 'props'
    */}
    {/* <App msg={message}/> */}
        <SomeComponent/>
  </React.StrictMode>
);

This code imports both the React and ReactDOM libraries, as well as the “SomeComponent” module from the same directory.

The code uses the “ReactDOM.createRoot()” method to render an instance of the “SomeComponent” component to the element with an ID of “root” in the HTML document.

The “<SomeComponent />” syntax is a JSX expression that creates an instance of the “SomeComponent” component. This component can be thought of as a reusable UI element that can be rendered multiple times throughout the application.

The “ReactDOM.render()” method takes two arguments: the first argument is the JSX expression or component that should be rendered, and the second argument is the DOM element where the component should be rendered.

In this example, the “SomeComponent” component is rendered to the “root” element, which is assumed to be present in the HTML document. When the application is loaded, the browser will render the “SomeComponent” component inside the “root” element

2. Props:

class SomeComponent extends React.Component {
     constructor(props){
         super(props);
    } 
    render() {
        return <div>Hello {this.props.name}!</div>;
    }
}

ReactDOM.render(
    <SomeComponent name="DotNetCurry" />,
    document.getElementById('root')
);

This is an example of a React component called “SomeComponent” that uses a prop to dynamically render a personalized greeting.

The “SomeComponent” component extends the “React.Component” class and defines a “render” method that returns a JSX expression. The JSX expression is a div element that includes the text “Hello” followed by the value of the “name” prop passed into the component.

In this example, the “name” prop is set to “DotNetCurry” using the syntax “<SomeComponent name=”DotNetCurry” />”. This creates an instance of the “SomeComponent” component with the prop “name” set to “DotNetCurry”.

The “ReactDOM.render()” method is then used to render the “SomeComponent” component to the DOM, passing the instance of the component as the first argument and the element where it should be rendered as the second argument.

When the component is rendered, the browser will display the personalized greeting “Hello DotNetCurry!”.
Props are a powerful feature of React that allow components to be customized and reused in different contexts. By passing data and functionality down through props, components can be composed and combined to create complex user interfaces.

3. State:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
    }

    handleClick() {
        this.setState({ count: this.state.count + 1 });
    }

    render() {
        return (
            <div>
                <p>Count: {this.state.count}</p>
                <button onClick={() => this.handleClick()}>Click me</button>
            </div>
        );
    }
}

The given example demonstrates a React component called “SomeComponent” that utilizes state to keep track of and modify a count when a button is clicked.

The component defines a constructor that initializes its state with a count of 0. The constructor also binds the “handleClick” method to the component’s “this” context using the “bind()” method.

The “handleClick” method updates the count in the component’s state using the “setState” method, which causes a re-render of the component with the updated count.

The “render” method returns a JSX expression that includes a paragraph element displaying the current count and a button element with an “onClick” event handler that calls the “handleClick” method when the button is clicked.

Whenever the button is clicked, the “handleClick” method is executed, which modifies the count in the component’s state, causing a re-render of the component with the updated count displayed in the paragraph element.

By using state and event handlers, React components can respond to user interactions and update the UI dynamically. This approach allows for the creation of interactive and responsive user interfaces.

4. Conditional Rendering:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isLoggedIn: false };
    }

    render() {
        return (
            <div>
                {this.state.isLoggedIn ?
                    <p>Welcome back!</p> :
                    <button onClick={() => this.setState({ isLoggedIn: true })}>Log In</button>
                }
            </div>
        );
    }
}

The given code example is a React component named “SomeComponent” that demonstrates how state can be used to track the login status of a user.

In the component’s constructor, the state object is initialized with a property called “isLoggedIn” that is set to “false”. This means that when the component first renders, the user is not logged in.

The “render” method of the component contains a ternary operator that checks the value of “isLoggedIn” in the component’s state. If “isLoggedIn” is “true”, the component displays a paragraph element with a “Welcome back!” message. If “isLoggedIn” is “false”, the component displays a login button.

If the user is not logged in and clicks the login button, the “onClick” event handler is triggered. This calls the “setState” method, which updates the component’s state and sets “isLoggedIn” to “true”.
When the user clicks the login button, the “setState” method is called, and the “isLoggedIn” property is updated to “true”. This triggers a re-render of the component, and the “Welcome back!” message is displayed instead of the login button.

This is a simple example of how state can be used to create conditional rendering in React components. By updating the component’s state, the component can dynamically display different UI elements based on the state of the application.

5. Handling Events:

class SomeComponent extends React.Component {
    handleClick() {
        console.log('Button clicked!');
    }

    render() {
        return (
            <div>
                <button onClick={() => this.handleClick()}>Click me</button>
            </div>
        );
    }
}

The given code is an example of a React component called “SomeComponent” that defines an event handler function named “handleClick”.

The component’s “render” method displays a single button element with an “onClick” event handler that is set to call the “handleClick” function when the button is clicked.

When the button is clicked, the event handler function logs a message to the console using the “console.log” method.

This is a simple example of how event handling works in React components. By defining event handler functions and attaching them to UI elements using event handlers like “onClick”, React components can respond to user input and trigger specific behaviors in the application.

6. Forms:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { name: '' };
    }

    handleChange(event) {
        this.setState({ name: event.target.value });
    }

    handleSubmit(event) {
        event.preventDefault();
        console.log('Name submitted: ' + this.state.name);
    }

    render() {
        return (
            <form onSubmit={(event) => this.handleSubmit(event)}>
                <label>
                    Name:
                    <input type="text" value={this.state.name} onChange={(event) => this.handleChange(event)} />
                </label>
                <button type="submit">Submit</button>
            </form>
        );
    }
}

The given code is a React component called “SomeComponent” that demonstrates how to handle form input in React.

In the component’s constructor, the state object is initialized with a property called “name” that is set to an empty string. The state is used to store the data entered by the user in the input field of the form.

The component defines two event handler functions: “handleChange” and “handleSubmit”. The “handleChange” function is called whenever the value of the input field changes. This function updates the state of the component with the new value entered by the user. The “handleSubmit” function is called when the user clicks the submit button. This function logs the value of the input field to the console.

The component’s “render” method displays a form element with a label and an input field for the user to enter their name. The value of the input field is set to the value of the “name” property in the component’s state. The “onChange” event handler is attached to the input field to call the “handleChange” function whenever the user types in the input field.

The form also includes a submit button that triggers the “handleSubmit” function when clicked. The “onSubmit” event handler is attached to the form and calls the “handleSubmit” function when the form is submitted.

By tracking the state of the input field and updating the state when the user types in the input field, the component can respond to user input and submit the form with the user’s input when the submit button is clicked. This is a basic example of how to handle form input in React.

7. Lifecycle Methods:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        console.log('Component initialized.');
    }

    componentDidMount() {
        console.log('Component mounted.');
    }

    componentDidUpdate() {
        console.log('Component updated.');
    }

    componentWillUnmount() {
        console.log('Component unmounted.');
    }

    render() {
        return <div>Hello World!</div>;
    }
}

The given code is a React component called “SomeComponent” that demonstrates the use of lifecycle methods in React.
The constructor method is used to initialize the component’s state and logs a message to the console, informing that the component has been initialized.

The componentDidMount method is called once the component has been mounted to the DOM. This method is used to log a message to the console indicating that the component has been mounted successfully.

The componentDidUpdate method is called every time the component’s state or props have been updated. This method logs a message to the console indicating that the component has been updated.

The componentWillUnmount method is called before the component is removed from the DOM. This method logs a message to the console informing that the component is about to be unmounted.
The render method is used to describe how the component should appear on the DOM. In this case, the component is rendering a div that contains the text “Hello World!”.

By utilizing these lifecycle methods, the component can perform specific actions at different stages of its lifecycle. It allows the component to log messages to the console or execute cleanup procedures when the component is about to be unmounted.

8. Conditional Styling:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isHighlighted: false };
    }

    render() {
        const styles = {
            backgroundColor: this.state.isHighlighted ? 'yellow' : 'white'
        };

        return (
            <div style={styles}
                onMouseEnter={() => this.setState({ isHighlighted: true })}
                onMouseLeave={() => this.setState({ isHighlighted: false })}>
                Hover over me!
            </div>
        );
    }
}

The code showcases a React component called “SomeComponent” that changes its background color based on whether it is being hovered over or not.

The constructor method is used to initialize the component’s state. In this case, it sets the initial value of the “isHighlighted” state to false.

The render method is used to describe how the component should appear on the DOM. The method sets the background color of the div based on whether the component is being highlighted or not.

The “styles” constant contains an object that describes the styles for the div. If the “isHighlighted” state is true, the background color of the div is set to yellow. Otherwise, it is set to white.

The div element uses the “styles” constant to set its style dynamically. Additionally, two event handlers are used to update the “isHighlighted” state. When the mouse enters the div, the “isHighlighted” state is set to true, and when the mouse leaves the div, the “isHighlighted” state is set to false.

This behavior allows the component to be interactive, creating a more engaging user experience. It’s an example of how React components can be used to build responsive and dynamic user interfaces.

9. Handling Errors:

class SomeComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        console.error('Error:', error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return <div>Something went wrong.</div>;
        }

        return <div>Hello World!</div>;
    }
}

The above code snippet is a React component called SomeComponent. It has a constructor that sets the initial state of the component to have hasError property set to false.

SomeComponent also has two methods: getDerivedStateFromError and componentDidCatch. The getDerivedStateFromError method is a static method that gets called when an error is thrown from any component that is a child of this component. The method takes the error object as an argument and returns a new state object with hasError set to true.

The componentDidCatch method is called when an error is thrown by any of the child components of this component. It takes two arguments: error and errorInfo. The method logs the error and error info to the console.

The render method of the SomeComponent is responsible for rendering the component’s UI. If the hasError property of the state is true, it returns a simple error message, otherwise, it returns a div element with a text “Hello World!”.

Alternative Approach – Using Error Boundary component

The same example can be handled gracefully using the Error Boundary. Error Boundary is a component that is designed to catch and handle JavaScript errors that occur within its child components during rendering, lifecycle methods, or constructors. It allows developers to gracefully handle errors in their applications, preventing the entire app from crashing and displaying a meaningful fallback UI to the user.

To create an Error Boundary component, you need to define a class component that extends the React.Component class and implement the following two lifecycle methods:

static getDerivedStateFromError(error): This method is called when an error is thrown within a child component. It receives the error object as a parameter and should return an object representing the updated state of the Error Boundary component. This way, you can update the component’s state to indicate that an error has occurred, which can be used to display a fallback UI.

static getDerivedStateFromError(error) {
    return { hasError: true };
}

componentDidCatch(error, errorInfo): This method is called when an error is caught by the Error Boundary. It receives two parameters: the error object and an object containing additional information about the error. You can use this method to log the error or send it to an error monitoring service.

componentDidCatch(error, errorInfo) {
    console.error('Error caught in Error Boundary:', error, errorInfo);
    // You can also send the error information to an error monitoring service.
}

To rewrite your code using the Error Boundary component, you need to separate the Error Boundary functionality from the SomeComponent and create a dedicated Error Boundary component. Here’s how you can do it in 3 steps:

Step 1: Create an ErrorBoundary.js file to define the Error Boundary component:

// ErrorBoundary.js
import React, { Component } from 'react';

class ErrorBoundary extends Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, errorInfo) {
        console.error('Error:', error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return <div>Something went wrong.</div>;
        }

        return this.props.children;
    }
}

export default ErrorBoundary;

Step 2: Modify your SomeComponent to remove the Error Boundary functionality and only contain its own logic:

// SomeComponent.js
import React, { Component } from 'react';

class SomeComponent extends Component {
    render() {
        return <div>Hello World!</div>;
    }
}

export default SomeComponent;

Step 3: Wrap SomeComponent with the ErrorBoundary component in your main app or wherever you use SomeComponent:

 

import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import SomeComponent from './SomeComponent';

function App() {
    return (
        <ErrorBoundary>
            <SomeComponent />
        </ErrorBoundary>
    );
}

export default App;

Now, if an error occurs within the SomeComponent or its descendants, the Error Boundary will catch the error, display the fallback UI, and log the error information.

10. Fragments:

class SomeComponent extends React.Component {
    render() {
        return (
            <>
                <div>Hello</div>
                <div>World</div>
            </>
        );
    }
}

The code defines a React component called SomeComponent. It has a render() method that returns a JSX expression which consists of two div elements with the text “Hello” and “World”.

The JSX expression uses a React feature called fragments, denoted by the empty tag <>. Fragments allow you to return multiple elements from a component’s render() method without having to wrap them in a parent element like a div.

Using fragments can help improve the readability and maintainability of your code by avoiding unnecessary nesting of elements in the DOM.

11. Higher-Order Components:

function withLogging(WrappedComponent) {
    return class extends React.Component {
        componentDidMount() {
            console.log('Component mounted.');
        }

        componentDidUpdate() {
            console.log('Component updated.');
        }

        componentWillUnmount() {
            console.log('Component unmounted.');
        }

        render() {
            return <WrappedComponent {...this.props} />;
        }
    };
}

class SomeComponent extends React.Component {
    render() {
        return <div>Hello World!</div>;
    }
}

const LoggedComponent = withLogging(MyComponent);

The above code defines a higher-order component called withLogging, which takes a React component as an argument and returns a new component that logs when it is mounted, updated, or unmounted.

The withLogging function returns an anonymous class component that extends React.Component. It overrides the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods to log a message to the console. It then renders the WrappedComponent passed to it using the spread operator to pass through any props that were originally passed to the component.

The code also defines a SomeComponent class component that simply renders the text “Hello World!”.

To create a new component that logs its lifecycle methods, SomeComponent is passed to withLogging to create a new component called LoggedComponent. LoggedComponent is the same as SomeComponent, but with logging behavior added.

What’s a higher-order component?

A higher-order component (HOC) is a function that takes a component and returns a new component with additional functionality. It is a design pattern in React that allows code reuse and keeps the codebase clean and modular.

The HOC pattern is used to abstract common logic or behaviors and apply them to multiple components. For example, you might have several components that need to access the same data from a backend API. Instead of repeating the data fetching code in each component, you could create an HOC that fetches the data and passes it down to the components as props.

Another common use case for HOCs is to add common UI behavior, such as logging or analytics, to multiple components. By wrapping a component with an HOC, you can add the behavior to all instances of the component without modifying the component’s code directly.

To create an HOC, you define a function that takes a component as an argument and returns a new component that wraps the original component. The HOC can then add props, modify props, or add new behavior to the original component before rendering it.

By using higher-order components, you can avoid code duplication and keep your codebase organized and maintainable.

12. Context:

const MyContext = React.createContext();

class SomeComponent extends React.Component {
    render() {
        return (
            <MyContext.Provider value="Hello World!">
                <SomeChildComponent />
            </MyContext.Provider>
        );
    }
}

function SomeChildComponent() {
    return (
        <MyContext.Consumer>
            {value => <div>{value}</div>}
        </MyContext.Consumer>
    );
}

In the given code snippet, a context is created using React.createContext() and assigned to a constant named MyContext.

A class component named SomeComponent is defined which returns an element wrapped inside a <MyContext.Provider> component. The value prop of the <MyContext.Provider> component is set to “Hello World!”.

A function component named SomeChildComponent is defined which returns an element wrapped inside a <MyContext.Consumer> component. The value of the context is accessed using the render prop pattern. The value is then rendered inside a <div> tag.

In other words, the MyContext object is used to pass data between the SomeComponent and SomeChildComponent components without the need to pass props down through all levels of the component tree. This is useful when the data needs to be accessed by several components at different levels of the component tree.

Alternative Approach – using the useContext() hook

The code we just saw can be rewritten using the useContext() hook. Some advantages of using the useContext() hook are as follows:

  • Cleaner code: The useContext() hook simplifies the code by removing the need for a Consumer component, making it more readable and easier to understand.
  • Functional components: Using hooks allows you to write functional components instead of class components, which can lead to more concise and maintainable code.
  • Better performance: Functional components can have better performance than class components, especially when combined with other hooks like useMemo() and useCallback() to optimize rendering.
  • Consistency: If you’re already using hooks in your codebase, adopting useContext() for context management keeps your code consistent and more comfortable to work with.

To rewrite the given code using the useContext() hook, you can follow these three steps:

Step 1: Create a context using React.createContext():

const MyContext = React.createContext();

Step 2: Rewrite SomeComponent using a functional component and the useContext() hook:

import React from 'react';

const MyContext = React.createContext();

function SomeComponent() {
    return (
        <MyContext.Provider value="Hello World!">
            <SomeChildComponent />
        </MyContext.Provider>
    );
}

Step 3: Update SomeChildComponent to use the useContext() hook:

import React, { useContext } from 'react';

function SomeChildComponent() {
    const value = useContext(MyContext);
    return <div>{value}</div>;
}

Here’s the complete code:

import React, { useContext } from 'react';

const MyContext = React.createContext();

function SomeComponent() {
    return (
        <MyContext.Provider value="Hello World!">
            <SomeChildComponent />
        </MyContext.Provider>
    );
}

function SomeChildComponent() {
    const value = useContext(MyContext);
    return <div>{value}</div>;
}

13. React Hooks:

import React, { useState, useEffect } from 'react';

function SomeComponent() {
    const [count, setCount] = useState(0);

    useEffect(() => {
        document.title = `You clicked ${count} times`;
    });

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>
    );
}

The code snippet defines a React functional component SomeComponent.

It uses the useState hook to define a state variable count initialized to 0 and a setter function setCount to update the state.

It then uses the useEffect hook to perform a side effect after every render cycle. Here, it sets the document title with a template string that includes the current value of count.

Finally, it renders a div containing a paragraph displaying the current value of count and a button with an onClick handler that increments the count state by 1 using the setCount function.

What’s a React Hook?

A React Hook is a feature introduced in React 16.8 that allows functional components to use state and lifecycle methods. Hooks are used as an alternative to class components and provide a way to manage stateful logic in functional components.

Using Hooks, developers can use stateful logic in functional components without the need to convert them to class components. Hooks are used to manage state, perform side effects, and update the DOM.

The most commonly used hooks are useState and useEffect. useState is used to manage state in functional components and useEffect is used to perform side effects such as fetching data, updating the DOM, or subscribing to events. There are other hooks e.g. useReducer, useContext, useRef, useCallback, etc.

Hooks provide a simpler and more efficient way to manage state and perform side effects in functional components, which makes it easier to write and maintain React code.

14. React Router:

import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

function Home() {
    return <h2>Home</h2>;
}

function About() {
    return <h2>About</h2>;
}

function Topics() {
    return (
        <div>
            <h2>Topics</h2>
            <ul>
                <li>
                    <Link to="/topics/1">Topic 1</Link>
                </li>
                <li>
                    <Link to="/topics/2">Topic 2</Link>
                </li>
            </ul>

            <Route path="/topics/:id" component={Topic} />
            <Route exact path="/topics" render={() => <h3>Please select a topic.</h3>} />
        </div>
    );
}

function Topic({ match }) {
    return <h3>Topic {match.params.id}</h3>;
}

function App() {
    return (
        <Router>
            <div>
                <nav>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <Link to="/about">About</Link>
                        </li>
                        <li>
                            <Link to="/topics">Topics</Link>
                        </li>
                    </ul>
                </nav>

                <Route exact path="/" component={Home} />
                <Route path="/about" component={About} />
                <Route path="/topics" component={Topics} />
            </div>
        </Router>
    );
}

The code is a simple React application that uses the React Router library to implement client-side routing. The app has three components: Home, About, and Topics. The Topics component is a bit more complex than the others because it includes dynamic routing.

The Home and About components simply return a heading that says “Home” or “About”, respectively.

The Topics component is where most of the routing logic is implemented. It returns a heading that says “Topics”, followed by a list of links to two topic pages: Topic 1 and Topic 2.

The Topic component is where the dynamic routing happens. It takes a match object as a prop, which contains information about the current URL, including any URL parameters. In this case, the URL parameter is id, which is used to display the current topic.

The App component is the top-level component that defines the overall layout of the app. It uses the Router component from React Router to wrap the entire app, and it defines the navigation menu using Link components. The Routes for each component are defined within the Router component. The exact keyword in the Route component tells React Router to match the path exactly, rather than matching any sub-paths.

Upgrading to React Router v6

The code we saw earlier uses React Router v5.I deliberately used v5 as I still see many examples following v5, which probably means devs are still using Router v5 in their code.

React Router v6, which was released after v5, introduced some breaking changes and different syntax. For example:

  • Route no longer accepts render prop, and instead, you would use the element prop with a JSX element.
  • <Route> with nested routes inside a parent <Route> is the new way to define nested routes in v6.
  • useParams hook is introduced, which is more commonly used to access route parameters instead of passing match prop to the component.

Here’s the code rewritten using React Router v6:

import React from 'react';
import { BrowserRouter as Router, Route, Link, Outlet, useParams } from 'react-router-dom';

function Home() {
    return <h2>Home</h2>;
}

function About() {
    return <h2>About</h2>;
}

function Topics() {
    return (
        <div>
            <h2>Topics</h2>
            <ul>
                <li>
                    <Link to="1">Topic 1</Link>
                </li>
                <li>
                    <Link to="2">Topic 2</Link>
                </li>
            </ul>

            <Outlet />
        </div>
    );
}

function Topic() {
    const { id } = useParams();
    return <h3>Topic {id}</h3>;
}

function App() {
    return (
        <Router>
            <div>
                <nav>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <Link to="/about">About</Link>
                        </li>
                        <li>
                            <Link to="/topics">Topics</Link>
                        </li>
                    </ul>
                </nav>

                <Outlet>
                    <Route path="/" element={<Home />} index />
                    <Route path="/about" element={<About />} />
                    <Route path="/topics" element={<Topics />}>
                        <Route path=":id" element={<Topic />} />
                    </Route>
                </Outlet>
            </div>
        </Router>
    );
}

Changes in React Router v6:

1. Route component: The component and render props have been replaced with the element prop, which takes a JSX element.

2. Nested routes: In v6, nested routes are defined as children of the parent Route component. In the example above, the Topic route is defined as a child of the Topics route.

3. Outlet component: The new Outlet component is used to render child routes. In the example above, the Outlet component is used inside the Topics component to render the Topic component when a specific topic is selected. Additionally, the Outlet component is used in the App component to render the top-level routes.

4. useParams hook: React Router v6 introduced the useParams hook, which is used to access route parameters instead of passing the match prop to the component. In the example above, the Topic component uses the useParams hook to get the id parameter.

5. index prop: In v6, the index prop is used to define the default route when no other nested routes match. In the example above, the Home route has an index prop set, which makes it the default route when no other routes match.

As you can see, using React Router v6, you’ll benefit from cleaner syntax, and more intuitive handling of nested routes.

15. Redux:

import { createStore } from 'redux';

function reducer(state = { count: 0 }, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
}

const store = createStore(reducer);

store.subscribe(() => console.log(store.getState()));

store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'DECREMENT' });

The code snippet shows an example of how to use the redux library to manage the state of an application. The redux library is a predictable state container for JavaScript applications.

Firstly, the code imports the createStore function from the redux library. This function is used to create a store for the application.

The code then defines a reducer function that specifies how the state should be updated in response to different actions. The reducer function takes two arguments: the current state and an action.

In this example, the initial state of the application is set to an object with a count property equal to zero. The reducer function specifies that if an INCREMENT action is dispatched, the count property should be incremented by one. Similarly, if a DECREMENT action is dispatched, the count property should be decremented by one.

After defining the reducer, the code creates a store by passing the reducer function to the createStore function.

Next, the code subscribes to the store by calling the subscribe method and passing a callback function that logs the current state of the store whenever it changes.

Finally, the code dispatches several actions to the store using the dispatch method. Each action is an object that specifies the type of the action to be performed. In this example, the INCREMENT action is dispatched twice and the DECREMENT action is dispatched once.

What’s the redux library?

Redux is an open-source library for managing state in JavaScript applications. It’s commonly used in combination with React, but it can be used with any other JavaScript framework or library.

One of the main reasons Redux is used is to simplify the management of application state. In larger applications, it can be challenging to keep track of state changes and maintain consistency across multiple components. Redux provides a centralized store that holds the application state, making it easier to manage and access the state data from any part of the application.

Redux uses a unidirectional data flow, which means that data flows in one direction: from the view to the state, and from the state to the view. This helps to prevent inconsistencies and makes the application more predictable and easier to debug.

In Redux, state changes are made by dispatching actions, which are objects that describe what has happened in the application. Reducers then take the current state and the action and return a new state. This way, the state is always immutable, and it can be traced back to specific actions.

Overall, Redux is a powerful tool for managing state in JavaScript applications, and it can help make complex applications more maintainable and easier to work with.

16. React-Redux:

import React from 'react';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux';

function reducer(state = { count: 0 }, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        case 'DECREMENT':
            return { count: state.count - 1 };
        default:
            return state;
    }
}

const store = createStore(reducer);

function Counter({ count, increment, decrement }) {
    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={increment}>+</button>
            <button onClick={decrement}>-</button>
        </div>
    );
}

const ConnectedCounter = connect(
    state => ({ count: state.count }),
    {
        increment: () => ({ type: 'INCREMENT' }),
        decrement: () => ({ type: 'DECREMENT' })
    }
)(Counter);

function App() {
    return (
        <Provider store={store}>
            <ConnectedCounter />
        </Provider>
    );
}

The above code snippet is an example of using the React Redux library to manage state in a React application.

The Redux library helps to manage the state of an application in a predictable way by using a single immutable state tree.

The createStore function from the Redux library is used to create a Redux store which holds the application state.

A reducer function is passed to the createStore function which takes in the current state and an action as arguments, and returns a new state based on the action.

The connect function from the react-redux library is used to connect the Redux store to a React component. The connect function takes two arguments, a mapStateToProps function which maps the state to props, and a mapDispatchToProps object which maps the dispatch of an action to props.

The Counter component is a presentational component which takes in props and returns a UI.

The ConnectedCounter component is the same Counter component which is connected to the Redux store using the connect function.

Finally, the Provider component from the react-redux library is used to provide the Redux store to the entire application.

Note: With the latest version of React, we use useDispatcher() instead of mapDispatchToProps and useSelector() instead of mapStateToProps. Read more about it over here: https://react-redux.js.org/api/hooks

Is the React library different from React-Redux?

Redux is a standalone library for managing state in JavaScript applications, while React-Redux is a package that provides bindings between Redux and React.

Redux is responsible for managing the global state of the application, and it provides a set of APIs for dispatching actions, updating the state, and subscribing to state changes. React-Redux, on the other hand, provides a set of React components and hooks that allow React components to access the Redux store and dispatch actions.

React-Redux is commonly used in React applications as a way to simplify the process of integrating Redux with React. It provides a clean interface for managing state, and it reduces the amount of boilerplate code that is needed to manage state in a React application.

In summary, Redux is the library that provides the core state management functionality, while React-Redux provides a set of React-specific bindings that make it easier to use Redux in a React application.

17. Axios:

import axios from 'axios';

axios.get('/api/data')
    .then(response => console.log(response.data))
    .catch(error => console.error(error));

The code above uses the Axios library to make an HTTP GET request to a specified API endpoint.

Axios is a popular JavaScript library that allows you to send HTTP requests to web servers and perform various CRUD (Create, Read, Update, and Delete) operations.

In this example, the axios.get() method is used to make a GET request to the /api/data endpoint.

If the request is successful, the then() method is called with a callback function that logs the response.data to the console. The response.data contains the data returned by the API.

If the request fails, the catch() method is called with a callback function that logs the error to the console. The error object contains information about the error that occurred.

Where do we use this code in a React Component?

When making API calls or performing side effects in a React component, you should use the appropriate lifecycle method (for class components) or the useEffect hook (for functional components).

Class Component:

In a class component, you would use the componentDidMount() lifecycle method to make the API call. This method is called once, immediately after the component is mounted (inserted into the DOM tree). Here’s an example:

import React, { Component } from 'react';
import axios from 'axios';

class MyComponent extends Component {
    componentDidMount() {
        axios
            .get('/api/data')
            .then(response => console.log(response.data))
            .catch(error => console.error(error));
    }

    render() {
        // Render your component here
    }
}

Functional Component:

In a functional component, you would use the useEffect hook to make the API call. The useEffect hook allows you to perform side effects in function components. By passing an empty dependency array [], the effect will only run once, mimicking the behavior of componentDidMount() in class components. Here’s an example:

import React, { useEffect } from 'react';
import axios from 'axios';

function MyComponent() {
    useEffect(() => {
        axios
            .get('/api/data')
            .then(response => console.log(response.data))
            .catch(error => console.error(error));
    }, []);

    // Render your component here
}

In both cases, the API call is made when the component is mounted. The primary difference is the use of the componentDidMount() lifecycle method in class components and the useEffect hook in functional components.

Functional components with hooks are generally recommended as they lead to cleaner and more maintainable code.

18. React Bootstrap:

import React from 'react';
import { Button } from 'react-bootstrap';

function SomeComponent() {
    return (
        <div>
            <h1>Hello, world!</h1>
            <Button variant="primary">Click me!</Button>
        </div>
    );
}

The code is defining a functional React component named MyComponent that renders a div containing an h1 element and a Button element from the react-bootstrap library.

The h1 element displays the text “Hello, world!” and the Button element displays the text “Click me!” with a variant prop set to “primary” to style the button with a blue color.

This code demonstrates the use of React and the react-bootstrap library to create a simple UI component with a styled button.

19. Material-UI:

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const useStyles = makeStyles(theme => ({
    root: {
        '& > *': {
            margin: theme.spacing(1),
        },
    },
}));

function SomeComponent() {
    const classes = useStyles();

    return (
        <div className={classes.root}>
            <h1>Hello, world!</h1>
            <Button variant="contained" color="primary">
                Click me!
            </Button>
        </div>
    );
}

The above code imports the necessary libraries for creating a component with a button. It uses the Material-UI library, which provides pre-built UI components that follow Google’s Material Design guidelines.

The makeStyles function is a styling solution from Material-UI that allows you to define styles for your components using a CSS-in-JS approach. It takes a function that receives the theme object as an argument and returns an object containing style rules.

In the code, the useStyles function is called to get the classes object, which contains the styles for the component. It defines a root property that applies a margin of one spacing unit to all child elements.

The SomeComponent function is the main component in the code. It renders a div element with the root class and contains a h1 element with the text “Hello, world!” and a Button component from Material-UI. The Button component has a variant of “contained” and a color of “primary”, and displays the text “Click me!”.

20. Formik:

import React from 'react';
import { Formik, Form, Field } from 'formik';

function MyForm() {
    return (
        <div>
            <h1>Sign Up</h1>
            <Formik
                initialValues={{ firstName: '', lastName: '', email: '' }}
                onSubmit={values => console.log(values)}
            >
                {({ isSubmitting }) => (
                    <Form>
                        <Field type="text" name="firstName" placeholder="First Name" />
                        <Field type="text" name="lastName" placeholder="Last Name" />
                        <Field type="email" name="email" placeholder="Email" />
                        <button type="submit" disabled={isSubmitting}>Submit</button>
                    </Form>
                )}
            </Formik>
        </div>
    );
}

The above code is an example of how to use the Formik library with React to create a form that collects user input data.

First, the Formik component is imported along with the Form and Field components. These will be used to create the form.

Next, a functional component called MyForm is defined. Inside this component, a simple heading is displayed and the Formik component is used to create the form.

The Formik component takes in an initialValues object that sets the initial values of the form fields. In this case, the form has fields for first name, last name, and email, all initially set to empty strings.

The onSubmit function is also defined, which logs the values of the form fields to the console when the form is submitted.

Inside the Formik component, there is a function that takes in a parameter called isSubmitting, which is used to disable the submit button when the form is being submitted.

The actual form is created using the Form component and the input fields are created using the Field component. The type of each field is specified using the type prop and the name of each field is specified using the name prop.

Finally, a submit button is created with the type set to “submit” and the disabled prop set to the value of isSubmitting.

Overall, this code demonstrates how to use Formik with React to create a simple form with input fields and a submit button.

21. Yup:

import * as Yup from 'yup';

const schema = Yup.object().shape({
    firstName: Yup.string().required('First Name is required'),
    lastName: Yup.string().required('Last Name is required'),
    email: Yup.string().email('Invalid email').required('Email is required')
});

function MyForm() {
    return (
        <div>
            <h1>Sign Up</h1>
            <Formik
                initialValues={{ firstName: '', lastName: '', email: '' }}
                validationSchema={schema}
                onSubmit={values => console.log(values)}
            >
                {({ isSubmitting }) => (
                    <Form>
                        <Field type="text" name="firstName" placeholder="First Name" />
                        <ErrorMessage name="firstName" />

                        <Field type="text" name="lastName" placeholder="Last Name" />
                        <ErrorMessage name="lastName" />

                        <Field type="email" name="email" placeholder="Email" />
                        <ErrorMessage name="email" />

                        <button type="submit" disabled={isSubmitting}>Submit</button>
                    </Form>
                )}
            </Formik>
        </div>
    );
}

function ErrorMessage({ name }) {
    return (
        <Field name={name}>
            {({ meta }) => meta.touched && meta.error ? (
                <div>{meta.error}</div>
            ) : null}
        </Field>
    );
}

The given code defines a form validation schema using the Yup library. The schema defines three fields: firstName, lastName, and email, with appropriate validation rules for each field. The required validation rule ensures that the user enters a value for the required fields, while the email validation rule checks whether the entered email is in a valid format or not.

In the code, the MyForm component renders a sign-up form that uses Formik for form management and validation. The initialValues prop initializes the values of the form fields to an empty string. The validationSchema prop passes the previously defined schema to the Formik component for validation.

Within the Formik component, a Form element is rendered, containing three Field elements for each of the form fields, and an ErrorMessage component that renders an error message if the field is touched and has an error.

The ErrorMessage component is defined separately and receives the name prop of the form field it is associated with. It uses the Field component from Formik to access the meta object that contains information about the field’s state, and renders an error message if the field is touched and has an error.

Overall, this code provides a simple and effective way to validate form inputs using Yup and Formik.

Yup is a JavaScript schema validation library that allows you to define a schema for your data and validate it against that schema. It is commonly used in conjunction with form libraries like Formik to validate form input values.

What is Yup?

Yup is a JavaScript library for schema validation. It allows you to define a schema object that represents the structure of your data and apply validation rules to it. Yup is often used in conjunction with form libraries such as Formik and React Hook Form to validate user input.

To use Yup, you first create a schema object using the Yup.object() method, and then define the schema fields using methods like Yup.string(), Yup.number(), and Yup.boolean(). You can then chain validation methods such as required(), min(), and max() to define the validation rules for each field.

Once you have defined your schema, you can use it to validate data using the validate() or validateSync() methods. These methods return a promise that resolves to the validated data or an error object if the data fails validation.

Overall, Yup is a powerful tool for validating data and can help improve the quality and reliability of your applications.

22. React Testing Library:

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import SomeComponent from './SomeComponent';

test('renders text and button', () => {
    const { getByText, getByRole } = render(<MyComponent />);
    const textElement = getByText(/Hello/i);
    const buttonElement = getByRole('button', { name: /Click me/i });
    expect(textElement).toBeInTheDocument();
    expect(buttonElement).toBeInTheDocument();
});

test('button click increments count', () => {
    const { getByRole, getByText } = render(<MyComponent />);
    const buttonElement = getByRole('button', { name: /Click me/i });
    fireEvent.click(buttonElement);
    const countElement = getByText(/Count: 1/i);
    expect(countElement).toBeInTheDocument();
});

The above code is a test file written using the Jest and Testing Library frameworks for a component called SomeComponent.

The code above shows two unit tests written using the @testing-library/react library to test a React component named SomeComponent. The tests aim to ensure that the component renders the expected elements and behaves correctly when user interacts with it.

The first test checks if the component renders a specific text and button element. To do this, it uses the render() function from @testing-library/react to render the component and then uses getByText() and getByRole() methods to find the elements with specific text and role. It then uses the expect() function to assert that the elements are in the document.

The second test checks if the component updates a count when the button is clicked. It follows a similar approach as the first test, but this time it simulates a click on the button using the fireEvent.click() method from @testing-library/react. It then asserts that the count is updated in the document by finding the element with the updated count using the getByText() method and using expect() to check that it is in the document.

Unit tests are an important part of software development as they help ensure that the code is functioning as intended and can catch errors early in the development process. The @testing-library/react library is a popular tool for testing React components as it provides a simple and intuitive API for interacting with components and querying the DOM.

23. Jest:

function add(a, b) {
    return a + b;
}

test('addition', () => {
    expect(add(2, 2)).toBe(4);
});

Jest is a popular testing framework for JavaScript that allows developers to write and run tests for their code, helping to ensure its correctness and reliability.

The above code snippet defines a simple JavaScript function called add that takes two arguments and returns their sum.

The next lines of code define a Jest test case that verifies the correctness of the add function.

The test function takes two arguments: a string description of the test case and a function that contains the test code.

In this case, the test code calls the add function with the arguments 2 and 2 and uses the Jest expect function to assert that the result of the addition should be 4.

If the result matches the expected value, the test passes. If not, the test fails and Jest outputs an error message.

24. Enzyme:

import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';

describe('<SomeComponent />', () => {
    it('renders text and button', () => {
        const wrapper = shallow(<SomeComponent />);
        expect(wrapper.contains(<h1>Hello, world!</h1>)).toBe(true);
        expect(wrapper.containsMatchingElement(<button>Click me!</button>)).toBe(true);
    });

    it('button click increments count', () => {
        const wrapper = shallow(<SomeComponent />);
        const button = wrapper.find('button');
        button.simulate('click');
        expect(wrapper.find('p').text()).toBe('Count: 1');
    });
});

The above code is a unit test for a React component called SomeComponent. The test uses Enzyme, a testing utility for React, to create a shallow rendering of the component and test its behavior.

The describe function is used to group together related tests. In this case, the group is called <SomeComponent />.

The first test in the group checks if the component renders a <h1> element with the text “Hello, world!” and a <button> element with the text “Click me!”. It uses the shallow function from Enzyme to create a shallow rendering of the component and then checks if the expected elements are present using the contains and containsMatchingElement methods.

The second test checks if clicking the button increments a count value. It creates a shallow rendering of the component, finds the button element using the find method, simulates a click event using the simulate method, and then checks if a <p> element with the text “Count: 1” is present using the text method of the Enzyme wrapper.

Overall, these tests help ensure that the SomeComponent component behaves as expected and that any changes made to it in the future do not break its existing functionality.

Enzyme is used with Jest to provide a set of utility functions for testing React components. Enzyme provides an API for rendering and manipulating React components in tests, making it easier to write unit tests and integration tests for React applications.

Jest itself provides basic functionality for testing React components, but Enzyme extends that functionality by providing additional tools for shallow rendering, mounting, and testing the output of components. With Enzyme, you can test React components in isolation or in combination with other components, and simulate user interactions to test component behavior.

Note: Once React released version 18, it was found that it is not compatible with Enzyme. Hence an alternative approach is to use React Testing Library.

Is there a Part 2 of this guide?

YES! Some additional topics that we will cover in the upcoming Part 2 of this guide includes:

  • Server-Side Rendering: rendering the initial HTML on the server instead of the client-side for better performance and SEO.
  • Code Splitting: divide your code into smaller chunks, which are loaded on-demand, improving the overall performance of your application.
  • Error Boundaries: catch and handle errors that occur during rendering, preventing your entire application from crashing.
  • React Performance: improve the performance of your React application, such as optimizing rendering, reducing re-renders, and minimizing the use of the virtual DOM.
  • Serverless Functions: execute code on the server-side without the need for a dedicated server, making it easier to build scalable and cost-effective applications.
  • GraphQL: fetch only the data you need, reducing the amount of data transferred over the network and improving the performance of your application.
  • React Native: framework for building mobile applications using React, allowing you to build cross-platform apps for iOS and Android using a single codebase.
  • Testing React Applications: different techniques and tools for testing React applications, including unit testing, integration testing, and end-to-end testing.
  • Deploying React apps

Do you need us to cover any more topics in Part 2?

Ping me on twitter, LinkedIn or via Email (suprotimagarwal[at]dotnetcurry) and let me know your suggestions.

More on JavaScript and React.js?

Fill up your details over here and starting April 2023, you’ll receive chapters from our upcoming guides Getting Started with JavaScript: Beginner to Advanced(Multi Part Series) and Getting Started with React: Beginner to Advanced(Multi Part Series) starting April 2023 in your inbox.

PDF Download

Download this guide as a PDF.

This article was technically reviewed by Mahesh Sabnis.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!