tfs-demos

React guide

These notes are not intended as a comprehensive guide to the topic. Their purpose is to guide you through the main areas you should learn about, with resources provided for further exploration. The goal is for you to learn enough to complete the associated challenge.

Introducing Single Page Applications (SPAs)

Further Learning


Introducing React

Further Learning


Installing Node.js and npm

What is it?

Why do we need it?

How do we use it?

  1. Visit https://nodejs.org
  2. Install the LTS version of Node
  3. Run these commands to check that it has worked:
    node -v
    npm -v
    
  4. Create a test folder and initialise npm:
    mkdir test-project
    cd test-project
    npm init -y
    
  5. Check that a package.json file has been created

Further Learning


Installing React with Vite, and create a “hello world” app

What is it?

Why do we need it?

How do we use it?

  1. Create a new Vite app:
    npm create vite@latest my-react-app
    
  2. Choose React
  3. Move into the new folder and install dependencies:
    cd my-react-app
    npm install
    
  4. Run the development server:
    npm run dev
    
  5. Open the local server URL (shown in terminal, e.g. http://localhost:5173/) and confirm the React starter page loads
  6. Edit src/App.jsx and check the browser updates accordingly

Further Learning


JSX

What is it?

Why do we need it?

How do we use it?

  1. Write JSX inside a React component file, usually with a .jsx file extension (or .tsx if it’s TypeScript)
  2. Each component returns a single root element
  3. Embed JavaScript within JSX using {} braces.
    function Greeting({ name }) {
      return <h1 className="title">Hello, {name}</h1>
    }
    
    export default Greeting
    

Further Learning


Components

What is it?

Why do we need it?

How do we use it?

  1. Create a new file in the src folder, for example Header.jsx
  2. Define a function that returns JSX
    function Header() {
      return (
        <header>
          <h1>My React App</h1>
        </header>
      )
    }
    
    export default Header
    
  3. Import and use the component inside another file, such as App.jsx
    import Header from './Header'
    
    function App() {
      return (
        <div>
          <Header />
          <p>Welcome to the site</p>
        </div>
      )
    }
    
    export default App
    
  4. Components must begin with a capital letter to be recognised by React

Further Learning


Props

What is it?

Why do we need it?

How do we use it?

  1. Create a new file called Welcome.jsx
  2. Define a component that accepts props
    function Welcome({ name }) {
      return <h2>Hello, {name}</h2>
    }
    
    export default Welcome
    
  3. Import and use it inside App.jsx
    import Welcome from './Welcome'
    
    function App() {
      return (
        <div>
          <Welcome name="Alice" />
          <Welcome name="Bob" />
        </div>
      )
    }
    
    export default App
    
  4. Confirm each instance displays a different greeting

Further Learning


Handling Events

What is it?

Why do we need it?

How do we use it?

  1. Create a new file ButtonExample.jsx
  2. Define a component with a button and an event handler
    function ButtonExample() {
      function handleClick() {
        alert('Button clicked')
      }
    
      return <button onClick={handleClick}>Click me</button>
    }
    
    export default ButtonExample
    
  3. Import and render the component inside App.jsx
  4. Test that clicking the button shows an alert
  5. Try using other events such as onChange, onMouseEnter, or onSubmit

Further Learning


Hooks

What is it?

Why do we need it?

How do we use it?

  1. Import the Hook you need from React
  2. Call the Hook inside your component’s function (never in loops, conditions, or nested functions)
    import { useState } from 'react'
    
    function Example() {
      const [text, setText] = useState('Hello')
      return <p>{text}</p>
    }
    
    export default Example
    

Further Learning


useState

What is it?

Why do we need it?

How do we use it?

  1. Import useState from React
  2. Create a state variable and a function to update it
    import { useState } from 'react'
    
    function Counter() {
      const [count, setCount] = useState(0)
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increase</button>
        </div>
      )
    }
    
    export default Counter
    
  3. Add more controls to decrease or reset the count
  4. Each state update triggers a re-render

Further Learning


useEffect

What is it?

Why do we need it?

How do we use it?

  1. Import useEffect from React
  2. Write an effect that runs after render
    import { useEffect, useState } from 'react'
    
    function Timer() {
      const [count, setCount] = useState(0)
    
      useEffect(() => {
        const interval = setInterval(() => setCount(c => c + 1), 1000)
        return () => clearInterval(interval)
      }, [])
    
      return <p>Seconds: {count}</p>
    }
    
    export default Timer
    
  3. The empty array [] ensures the effect only runs once when the component mounts

Further Learning


Conditional Rendering

What is it?

Why do we need it?

How do we use it?

function Message({ loggedIn }) {
  return (
    <div>
      {loggedIn ? <p>Welcome back</p> : <p>Please log in</p>}
    </div>
  )
}

export default Message

Further Learning


Loops with .map

What is it?

Why do we need it?

How do we use it?

function List() {
  const items = ['Apple', 'Banana', 'Cherry']

  return (
    <ul>
      {items.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  )
}

export default List

Further Learning


Using Forms

What is it?

Why do we need it?

How do we use it?

import { useState } from 'react'

function FormExample() {
  const [name, setName] = useState('')

  function handleSubmit(e) {
    e.preventDefault()
    alert(`Hello ${name}`)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={e => setName(e.target.value)} />
      <button type="submit">Submit</button>
    </form>
  )
}

export default FormExample

Further Learning


Prop Drilling and Lifting State

What is it?

Why do we need it?

How do we use it?

function Parent() {
  const [message, setMessage] = useState('Hello')

  return <Child message={message} />
}

function Child({ message }) {
  return <p>{message}</p>
}

Further Learning


useContext

What is it?

Why do we need it?

How do we use it?

import { createContext, useContext } from 'react'

const ThemeContext = createContext('light')

function Child() {
  const theme = useContext(ThemeContext)
  return <p>Theme: {theme}</p>
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Child />
    </ThemeContext.Provider>
  )
}

export default App

Further Learning


Async Data Fetching

What is it?

Why do we need it?

How do we use it?

import { useEffect, useState } from 'react'

function Users() {
  const [users, setUsers] = useState([])

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(data => setUsers(data))
  }, [])

  return (
    <ul>
      {users.map(u => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  )
}

export default Users

Further Learning


Using third party packages

Small, focused packages can save time and reduce bugs. Add them when they clearly improve readability or remove boilerplate. Keep the number small and review them regularly

A simple example to add now

Format dates with date-fns

  1. Install
    npm install date-fns
    
  2. Create a tiny helper so your components stay clean src/lib/formatDate.js
    import { parseISO, format } from 'date-fns'
    
    export function formatDate(isoString) {
      try {
        return format(parseISO(isoString), 'd LLL yyyy')
      } catch {
        return isoString
      }
    }
    
  3. Use it in any component that shows a date src/components/Users/Users.jsx
    import { useEffect, useState } from 'react'
    import { formatDate } from '../../lib/formatDate'
    
    export default function Users() {
      const [users, setUsers] = useState([])
    
      useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/users')
          .then(r => r.json())
          .then(setUsers)
      }, [])
    
      return (
        <ul>
          {users.map(u => (
            <li key={u.id}>
              <strong>{u.name}</strong>
              <span> joined {formatDate('2020-05-01')}</span>
            </li>
          ))}
        </ul>
      )
    }
    

Where it goes

Good habits with packages


Routing

What is it?

Why do we need it?

How do we use it?

import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
import Home from './Home'
import About from './About'

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  )
}

export default App

Further Learning


Styling Approaches

What is it?

Why do we need it?

How do we use it?

import './App.css'

function App() {
  return <h1 className="title">Hello React</h1>
}

export default App

Further Learning


Debugging with React Dev Tools

What is it?

Why do we need it?

How do we use it?

  1. Install React Developer Tools for your browser
  2. Open the React tab in DevTools while your app is running
  3. Select a component to inspect its props, state, and rendered output

Further Learning


Deploying to Netlify

What is it?

Why do we need it?

How do we use it?

  1. Push your project to a GitHub repository
  2. Go to Netlify and connect your repository
  3. Choose the Vite build command: npm run build and publish directory: dist
  4. Netlify builds and deploys automatically

Further Learning


State Management (Redux Toolkit or Zustand)

What is it?

Why do we need it?

How do we use it?

import { configureStore, createSlice } from '@reduxjs/toolkit'
import { Provider, useSelector, useDispatch } from 'react-redux'

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: state => { state.value += 1 }
  }
})

const store = configureStore({ reducer: { counter: counterSlice.reducer } })

function Counter() {
  const count = useSelector(s => s.counter.value)
  const dispatch = useDispatch()
  return <button onClick={() => dispatch(counterSlice.actions.increment())}>{count}</button>
}

export default function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  )
}

Further Learning


Thunks

What is it?

Why do we need it?

How do we use it?

import { createAsyncThunk, createSlice, configureStore } from '@reduxjs/toolkit'

const fetchUsers = createAsyncThunk('users/fetch', async () => {
  const res = await fetch('https://jsonplaceholder.typicode.com/users')
  return res.json()
})

const usersSlice = createSlice({
  name: 'users',
  initialState: [],
  extraReducers: builder => {
    builder.addCase(fetchUsers.fulfilled, (state, action) => action.payload)
  }
})

const store = configureStore({ reducer: { users: usersSlice.reducer } })

Further Learning


Auth

What is it?

Why do we need it?

How do we use it?

Further Learning


Environment variables

APIs often need keys. Do not hard code secrets in your codebase. Instead you can keep them in an .env file that you don’t commit to git

Instead you can create an example .env.example file that you do commit to git. This file contains the names of environmental variables you use in your app, but not their values

If you don’t already have one, add a .gitignore file in the root of your repo, and add the following line:

.env

Next, create a .env file at the project root and add any important values, such as:

VITE_API_BASE=https://api.example.com

Note that when using Vite, environmental variable names must start with VITE_API_

You can then read it in your JavaScript code with import.meta.env.VITE_API_BASE when using Vite:

const apiUrl = import.meta.env.VITE_API_BASE;

Further learning


Error boundaries

Catch render errors and show a friendly message rather than a blank screen

Further learning


Testing the UI

Add one or two tests to prove behaviour

Example:

import { render, screen } from '@testing-library/react'
import Greeting from './Greeting'

test('renders greeting', () => {
  render(<Greeting name="Alice" />)
  expect(screen.getByText(/Alice/)).toBeInTheDocument()
})