React People Setup, Index, Create

Setup

  • open terminal in frontend folder
  • install react router and sass:
  npm install react-router-dom sass
  • create a file called styles.scss in the /src folder

Installing Router and Sass

With React-Router we will be able to create Routes, Loaders and Actions:

  • Routes: A component that render when navigate to a particular URL
  • Loaders: A function to get data that runs before a route loads and can be used in a component with the useLoaderData hook
  • Actions: Functions that run if a Form component is submitted to a particular route.

To keep track of these create three files in your src folder

  • router.js
  • loaders.js
  • actions.js

In router.js let's setup our router:

import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
} from "react-router-dom"
import App from "./App"

const router = createBrowserRouter(
  createRoutesFromElements(<Route path="/" element={<App />}></Route>)
)

export default router
  • Update index.js to like like so
import React from "react"
import ReactDOM from "react-dom/client"
import "./styles.scss"
import reportWebVitals from "./reportWebVitals"
import { RouterProvider } from "react-router-dom"
import router from "./router"

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()

Scoping Out Our Components

  • Create a components and pages folder
  • In the components folder create a Header.js file
  • In the pages folder create a Index.js and Show.js folder
  • Write the component boilerplate and export the component in all the created files
function Component(props) {
  return <h1>Component Name</h1>
}

export default Component

App.js

Our desired component Architecture

-> App
  -> Header
  -> Outlet
      -> Route |path: "/"|
        -> Index |loads all people|
      -> Route |path="/people/:id|
        -> Show |loads single person|
      -> Route |path: "/create"| Action to create people
      -> Route |path: "/update/:id"| Action to update people
      -> Route |path: "/delete/:id"| Action to delete people

Let's update our routes in router.js:

import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
} from "react-router-dom"
import App from "./App"
import Index from "./pages/Index"
import Show from "./pages/Show"

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<App />}>
      <Route path="" element={<Index />} />
      <Route path=":id" element={<Show />} />
      <Route path="create" />
      <Route path="update/:id" />
      <Route path="delete/:id" />
    </Route>
  )
)

export default router

Let's add the following to App.js

import { Outlet } from "react-router-dom"
import Header from "./components/Header"

function App() {
  return (
    <div className="App">
      <Header />
      <Outlet />
    </div>
  )
}

export default App

Setting up router in Main.js

  • let's create our routes

Setting Up Navigation

Let's put the following in Header.js

import { Link } from "react-router-dom"

function Header(props) {
  return (
    <nav className="nav">
      <Link to="/">
        <div>People App</div>
      </Link>
    </nav>
  )
}

export default Header

Sass

Sass is a CSS pre-compiler that allows us some new tricks in writing CSS including...

  • Nesting
  • Mixin
  • Variables

Let's write some Sass in our styles.scss

// --------------------------
// VARIABLES
// --------------------------
$maincolor: black;
$contrastcolor: white;

@mixin white-text-black-bg {
  color: $contrastcolor;
  background-color: $maincolor;
}

@mixin black-test-white-bg {
  color: $maincolor;
  background-color: $contrastcolor;
}

// --------------------------
// Header
// --------------------------

nav {
  @include white-text-black-bg;
  display: flex;
  justify-content: flex-start;

  a {
    @include white-text-black-bg;
    div {
      margin: 10px;
      font-size: large;
    }
  }
}

Displaying People in Index

First we will give the index route a loader to load all the people who should be listed. This is how src/loaders.js should look like:

const URL = "http://localhost:4000"

export const peopleLoader = async () => {
  const response = await fetch(URL + "/people")
  const people = await response.json()
  return people
}

Then we attach this loader to our route in router.js

import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
} from "react-router-dom"
import App from "./App"
import Index from "./pages/Index"
import Show from "./pages/Show"
import { peopleLoader } from "./loaders"

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<App />}>
      <Route path="" element={<Index />} loader={peopleLoader} />
      <Route path=":id" element={<Show />} />
      <Route path="create" />
      <Route path="update/:id" />
      <Route path="delete/:id" />
    </Route>
  )
)

export default router

Let's now display the people in Index.js

import { Link, useLoaderData } from "react-router-dom"

function Index(props) {
  const people = useLoaderData()

  return people.map(person => (
    <div key={person._id} className="person">
      <Link to={`/${person._id}`}>
        <h1>{person.name}</h1>
      </Link>
      <img src={person.image} alt={person.name} />
      <h3>{person.title}</h3>
    </div>
  ))
}

export default Index

Creating People

Let's now add a form to our index.js

  • Use the react-router form component which triggers a route actions when submitted
  • We will create an action that create a person from the form data with our API
import { Form, Link, useLoaderData } from "react-router-dom"

function Index(props) {
  const people = useLoaderData()

  return (
    <div>
      <h2>Create a Person</h2>
      <Form action="/create" method="post">
        <input type="input" name="name" placeholder="person's name" />
        <input type="input" name="image" placeholder="person's picture" />
        <input type="input" name="title" placeholder="person's title" />
        <input type="submit" value="create person" />
      </Form>

      <h2>People</h2>
      {people.map(person => (
        <div key={person._id} className="person">
          <Link to={`/${person._id}`}>
            <h1>{person.name}</h1>
          </Link>
          <img src={person.image} alt={person.name} />
          <h3>{person.title}</h3>
        </div>
      ))}
    </div>
  )
}

export default Index

Now we just need to create an action for when that form is submitted for the /create route. Let's add the following to actions.js:

import { redirect } from "react-router-dom"

const URL = "http://localhost:4000"

export const createAction = async ({ request }) => {
  // get data from form
  const formData = await request.formData()
  // set up our new person to match schema
  const newPerson = {
    name: formData.get("name"),
    image: formData.get("image"),
    title: formData.get("title"),
  }
  // Send new person to our API
  await fetch(URL + "/people", {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(newPerson),
  })
  // redirect to index
  return redirect("/")
}

Now let's attach this action to the right route in router.js:

import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
} from "react-router-dom"
import App from "./App"
import Index from "./pages/Index"
import Show from "./pages/Show"
import { peopleLoader } from "./loaders"
import { createAction } from "./actions"

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<App />}>
      <Route path="" element={<Index />} loader={peopleLoader} />
      <Route path=":id" element={<Show />} />
      <Route path="create" action={createAction} />
      <Route path="update/:id" />
      <Route path="delete/:id" />
    </Route>
  )
)

export default router

Conclusion

You should now be able to see all the people and create people

Lab

Begin the Frontend for your Cheese app, and create the ability to display and create cheeses like our People app.