Nested Routes in React Router

Imagine we want to have the nested routing for User page via tabs.

function App() {
    return (
        <div>
            <Routes>
                <Route path="/" element={<Home/>}/>
                <Route path="/user" element={<User/>}>
                    <Route path="account" element={<Account/>}/>
                    <Route path="profile" element={<Profile/>}/>
                </Route>
            </Routes>
        </div>
    );
}
function User() {
    return (
        <div>
            <h1>User Page</h1>
            <Link to="profile">Profile</Link>
            <br/>
            <Link to="account">Account</Link>
            <Outlet/>
        </div>
    );
}

The Outlet component will always render the next match.
The parent route (User) persists while the swaps between the two child routes (Profile and Account).

References
https://reactrouter.com/docs/en/v6/getting-started/tutorial#nested-routes
https://reactrouter.com/docs/en/v6/getting-started/concepts#outlets
https://ui.dev/react-router-nested-routes
https://www.robinwieruch.de/react-router-nested-routes/

Reading URL Params in React Router

App.tsx

function App() {
    return (
        <div>
            <Navbar/>
            <Routes>
                <Route path="/welcome" element={<Welcome/>}/>
                <Route path="/products" element={<Products/>}/>
                <Route path="/products/:product" element={<Product/>}/>
            </Routes>
        </div>
    );
}

Products.tsx

import {useParams} from 'react-router-dom'

function Product() {

    let params = useParams();

    return (
        <div>
            <strong>Product : </strong>{params.product}
        </div>
    );
}

export default Product;

References
https://reactrouter.com/docs/en/v6/getting-started/tutorial#reading-url-params

Display Active Links in React Router using NavLink

It’s very common, especially in navigation lists, to display the link as the active link the user is looking at. Let’s add this treatment to our invoices list by swapping out Link for NavLink.

import { NavLink, Outlet } from "react-router-dom";
import { getInvoices } from "../data";

export default function Invoices() {
  let invoices = getInvoices();
  return (
    <div style={{ display: "flex" }}>
      <nav
        style={{
          borderRight: "solid 1px",
          padding: "1rem",
        }}
      >
        {invoices.map((invoice) => (
          <NavLink
            style={({ isActive }) => {
              return {
                display: "block",
                margin: "1rem 0",
                color: isActive ? "red" : "",
              };
            }}
            to={`/invoices/${invoice.number}`}
            key={invoice.number}
          >
            {invoice.name}
          </NavLink>
        ))}
      </nav>
      <Outlet />
    </div>
  );
}

We changed the color of our link by looking at the isActive value that NavLink passed to our styling function.

You can do the same thing with className on NavLink:

// normal string
<NavLink className="red" />

// function
<NavLink className={({ isActive }) => isActive ? "red" : "blue"} />

References
https://reactrouter.com/docs/en/v6/getting-started/tutorial#active-links

Defining Routes in React Router

index.js

import * as React from "react";
import * as ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.js

import * as React from "react";
import { Routes, Route, Link } from "react-router-dom";
import "./App.css";

function App() {
  return (
    <div className="App">
      <h1>Welcome to React Router!</h1>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
      </Routes>
    </div>
  );
}

References
https://reactrouter.com/docs/en/v6/getting-started/installation#create-react-app

Run Code after React Component using Effect Hook

Effects Without Cleanup
we can run them and immediately forget about them.

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

function Example() {
  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>
  );
}

Effects with Cleanup

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

We don’t have to return a named function from the effect. We called it cleanup here to clarify its purpose, but you could return an arrow function or call it something different.

Use Multiple Effects to Separate Concerns

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // ...
}

Optimizing Performance by Skipping Effects

You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.

f you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument.

Always add everything you refer to inside of useEffect() as dependency.

References
https://reactjs.org/docs/hooks-effect.html

HTML Form Elements in React

import React, {useState} from "react";

function Info() {
    const [name, setName] = useState("");

    return (
        <div>
            <div>
                <label>
                    Name:
                    <input type="text" placeholder="Enter your name" value={name}
                           onChange={event => {
                               setName(event.target.value)
                           }}/>
                </label>

            </div>
            <div>
                Result : Hello {name}
            </div>

        </div>
    );
}

export default Info;

In most cases, it’s recommend using controlled components to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.

To write an uncontrolled component, instead of writing an event handler for every state update, you can use a ref to get form values from the DOM.

References
https://reactjs.org/docs/forms.html
https://reactjs.org/docs/uncontrolled-components.html
https://reactjs.org/docs/refs-and-the-dom.html