LogoLogo
  • FAC Curriculum
  • archive
    • Node & npm introduction
    • developer
      • Programming Paradigms
    • handbook
      • Asking for help
      • Coaching groups
      • Code review
      • Course rules
      • Debugging
      • Employment schedule and material delivered in FAC20
      • GitHub Workflow
      • Glossary or terms
      • Presentation Guidance
      • Equality, Diversity and Inclusion
      • Installation guide
      • Learning circles
      • Mentoring guidance
      • What to expect from your mentors
      • One-day projects
      • Pair programming
      • Portfolio
      • Questions for problem solving
      • Progress Logs
      • Final project report
      • Managing software projects
      • Project Presentations
      • Project roles
      • Projects
      • Retrospectives
      • Role Circles
      • Safeguarding policy
      • Technical Spikes
      • System requirements
      • Tech for Better
      • User Manuals
      • Wellbeing Support
      • project-docs
        • What makes a mentor?
        • Product Handover
        • Sprint Planning
        • Tech for Better Presentations
        • User Research & Usability Testing
    • foundation
      • full-stack
        • Learning Outcomes
        • project
      • testing
        • project
        • spikes
  • docs
    • Contributing to the curriculum
    • Curriculum intent
    • Curriculum process
  • src
    • About our curriculum
    • course
      • Code of Conduct
      • Docker
      • .NET and Umbraco
      • Getting started
      • Founders and Coders coursebook
      • KSB's
      • Mini projects
      • Revision checklist
      • Svelte
      • TypeScript
      • handbook
        • Software Developer Handbook
        • Software Foundation Handbook
      • precourse
        • Before you start the course
        • Installation List
      • syllabus
        • developer
          • app
            • learning-outcomes
            • project
            • schedule
            • spikes
          • introduction
            • learning-outcomes
            • project
            • resources
            • schedule
          • week00-pre-course
            • We'd like you to spend some time before starting the course working on useful fundamentals.
            • spikes
          • week01-project01-basics
            • Employability introduction
            • Homework
            • learning-outcomes
            • Week of September 9th
            • project
            • resources
            • schedule
            • spikes
          • week02-project02-chatbot
            • employability
            • Homework
            • learning-outcomes
            • Week of September 16th
            • project
            • resources
            • schedule
            • spikes
          • week03-project03-server
            • Learning Outcomes
            • Week of September 23th
            • The Amazin' Quizzer API Backend
            • resources
            • schedule
          • week04-project03-frontend
            • learning-outcomes
            • Week of September 30th
            • UI for Quizzer App
            • resources
            • schedule
          • week05-project03-test-deploy
            • Testing and deployment
            • Week of October 7th
            • project
            • resources
            • schedule
          • week06-project04-databases
            • learning-outcomes
            • Week of October 14th
            • project
            • Databases
            • schedule
          • week07-project04-authentication
            • Learning Outcomes
            • Week of October 21st
            • project
            • resources
            • schedule
          • week08-project04-test-deploy
            • Learning Outcomes
            • Week of October 28th
            • project
            • resources
            • schedule
          • week09-reading-week
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
          • week10-project05-DOTNET-intro
            • Learning Outcomes
            • overview
            • project
            • Resources
            • schedule
          • week11-project05-DOTNET-testing
            • Testing and deployment
            • Week of November 18th
            • project
            • Resources
            • schedule
          • week12-project05-DOTNET-deploy
            • Learning Outcomes
            • Week of November 25th
            • project
            • Resources
            • schedule
            • Spikes
          • week13-TFB-design
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
            • Design Week Spikes
          • week14-TFB-build
            • Learning Outcomes
            • overview
            • Project
            • DevOps Resources
            • schedule
            • Spikes
          • week15-TFB-build
            • Learning Outcomes
            • overview
            • Project
            • Resources
            • schedule
            • Spikes
          • projects
            • in-house-design
              • Learning Outcomes
              • Project
              • Resources
              • schedule
              • Design Week Spikes
        • foundation
          • Obsolete-full-stack
            • project
          • post-course
            • Homework
            • schedule
        • portfolio
          • fruit-shop
            • learning-outcomes
            • project
            • resources
          • game
            • learning-outcomes
            • project
            • resources
          • hobby-page
            • learning-outcomes
            • project
            • resources
          • movie-data
            • learning-outcomes
            • project
            • resources
          • project-gallery
            • learning-outcomes
            • project
            • resources
          • website
            • learning-outcomes
            • project
            • JavaScript
        • tfb
          • week 1
            • Introduction (45 minutes)
            • Further reading
          • week 10
            • content
            • resources
          • week 11
            • What will we be doing this week?
            • resources
          • week 12
            • What will we be doing this week?
            • Further reading
          • week 2
            • Discover (90 minutes)
            • resources
          • week 3
            • content
            • resources
          • week 4
            • Mapping the user journey (90 minutes)
            • resources
          • week 5
            • Figma Workshop 1 (90 minutes)
            • Further reading
          • week 6
            • Figma Workshop 2 (90 minutes)
            • resources
          • week 7
            • Product pitches & Selection (90 minutes)
            • resources
          • week 8
            • content
            • resources
          • week 9
            • content
            • resources
    • learn
      • DOTNET
        • Introduction to .NET
      • auth
        • Authenticating web apps
      • database
        • Persisting data with SQLite and Node
      • dotnet-two
        • Dependency injections and interfaces in .NET
      • form-validation
        • Form validation
      • react
        • Building client-side apps with React
      • server
        • HTTP servers with Node & Express
      • typescript
        • TypeScript
    • mentoring
      • design-week
        • Analysis Workshop
        • Code planning
        • Definition Workshop
        • Discovery Workshop
        • Figma introduction
        • Usability testing
        • User Research
    • resources
      • http
        • introduction
    • workshops
      • cookie-auth
        • index
      • creating-promises
        • index
      • css-layout
        • index
      • cypress-testing
        • index
      • database-testing
        • index
      • dev-tooling
        • Developer tooling
      • dom-challenge
        • index
      • dom-rendering
        • index
      • es-modules
        • index
      • express-middleware
        • Express middleware
      • first-class-functions
        • index
      • form-validation
        • index
      • functions-callbacks-async
        • Functions, callbacks, & async JavaScript
      • git-intro
        • Introduction to Git
      • git-terminal
        • Using Git in the terminal
      • git-workflow
        • Git workflow
      • github-projects
        • GitHub Projects Workflow Workshop
      • heroku-sql-challenge
        • index
      • html-forms
        • index
      • learn-a11y
        • index
        • starter-files
          • solution
            • Accessibility solution explanation
      • learn-fetch
        • index
      • learn-integration-testing
        • index
      • learn-testing
        • Learn testing in JavaScript
      • learn-unit-testing
        • index
      • node-error-handling
        • Node error-handling
      • node-express-server
        • Node and Express HTTP server
      • node-npm-intro
        • Node & npm introduction
      • node-postgres
        • Learn Postgres with Node
      • node-scripting-challenge
        • index
      • password-security
        • index
      • promise-practice
        • index
      • react-components
        • React components
      • react-fetch
        • index
      • react-forms
        • React forms
      • react-refactor-classes
        • index
      • react-state-effects
        • React state & effects
      • real-world-fetch
        • index
      • scope-challenge
        • Scope debugging challenge
      • semantic-html
        • index
      • server-side-forms
        • Server-side forms
      • session-auth
        • Session authentication
      • sql-intro
        • index
      • tdd-array-methods
        • index
Powered by GitBook
On this page
  • React state
  • Event listeners
  • Challenge 1
  • Side effects
  • Challenge 2
Export as PDF
  1. src
  2. workshops
  3. react-state-effects

React state & effects

Learn how to use the useState and useEffect hooks to create dynamic interactions in React

React is designed to build dynamic apps with lots of interaction. A common difficulty with apps like this is keeping the DOM up-to-date as the user interacts. React has two concepts to help keep this manageable: "state" and "effects".

React state

State is data that changes while your application is running. This might be in response to user actions, or after a fetch request finishes.

In React all stateful values are stored in JS as special variables. We can render our UI based on these variables—when they change React will automatically re-run the component function and update the DOM to reflect the new state value.

Using state

Imagine we have a counter component. When the button is clicked we want the count to go up one:

function Counter(props) {
  const count = 0;
  return <button>Count is {count}</button>;
}

We need some way to make our Counter function run again if this value changes.

The React.useState method can be used to create a "stateful" value. It takes the initial state value as an argument, and returns an array. This array contains two things: the state value, and a function that lets you update the state value.

function Counter(props) {
  const stateArray = React.useState(0);
  const count = stateArray[0];
  const setCount = stateArray[1];
  return <button>Count is {count}</button>;
}

It's common to use array destructuring to simplify this:

function Counter(props) {
  const [count, setCount] = React.useState(0);
  return <button>Count is {count}</button>;
}

The setCount function lets us update our state value and tells React to re-run this component. E.g. if we called setCount(10) React will call our Counter component function again, but this time the count variable would be 10 instead of 0.

This is how React keeps your UI in sync with the state.

Event listeners

We have a function that will let us update the state, but how do we attach event listeners to our DOM nodes?

function Counter(props) {
  const [count, setCount] = React.useState(0);
  function increment() {
    setCount(count + 1);
  }`
  return <button onClick={increment}>Count is {count}</button>;
}

You can pass event listener functions in JSX like any other property. They are always formatted as "on" followed by the camelCased event name. So "onClick", "onKeyDown", "onChange" etc.

In this example we are passing a function that calls setCount with our new value of count.

Challenge 1

Time to add some state! Open up challenge-1.html in your editor. You should see the Counter component we just created. This is an example; you can delete it if you want.

Create a new component called Toggle. It should render a button that toggles a boolean state value when clicked. It should also render a div containing its children, but only when the boolean state value is true.

Example usage:

function App() {
  return <Toggle>This text is hidden until the button is clicked</Toggle>;
}

Side effects

React is designed to make it easy to keep your application in sync with your data/state. Component functions render DOM elements and keep them in sync with any state values.

But most apps need more than just a UI—there are also things like fetching data from an API, timers/intervals, global event listeners etc. These are known as "side effects"—they can't be represented with JSX.

We need a way to ensure our effects reflect changes in state just like our UI does.

Using effects

React provides another "hook" like useState() for running side-effects after your component renders. It's called useEffect(). It takes a function as an argument, which will be run after every render (by default).

Let's say we want our counter component to also update the page title (so the count shows in the browser tab). There's no way to represent this update using the JSX our component returns. Instead we can use an effect:

function Counter(props) {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    document.title = `Count: ${count}`;
  });

  return <button onClick={() => setCount(count + 1)}>Count is {count}</button>;
}

React will run the arrow function we passed to useEffect() every time this component renders. Since calling setCount will trigger a re-render (as the state is updated) the page title will stay in sync with our state as the button is clicked.

Skipping effects

By default all the effects in a component will re-run after every render of that component. This ensures the effect always has the correct state values.

If your effect does something expensive/slow like fetching from an API (or sorting a massive array etc) then this could be a problem.

useEffect() takes a second argument to optimise when it re-runs: an array of dependencies for the effect. Any variable used inside your effect function should go into this array:

function Counter(props) {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>Count is {count}</button>;
}

Now our effect will only re-run if the value of count has changed.

Running effects once

Sometimes your effect will not be dependent on any props or state, and you only want it to run once (after the component renders the first time). In this case you can pass an empty array as the second argument to useEffect(), to signify that the effect has no dependencies and never needs to be re-run.

For example if we wanted our counter to increment when the "up" arrow key is pressed:

function Counter(props) {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    function handleKeyDown(event) {
      if (event.key === "ArrowUp") {
        setCount((prevCount) => prevCount + 1);
      }
    }
    window.addEventListener("keydown", handleKeyDown);
  }, []);

  return <div>Count is {count}</div>;
}

We add an event listener to the window, and pass an empty array to useEffect(). This will keep us from adding new event listeners every time count updates and triggers a re-render.

Cleaning up effects

Some effects need to be "cleaned up" if the component is removed from the page. For example timers need to be cancelled and global event listeners need to be removed. Otherwise you'd have a bunch of code running in the background trying to update a component that doesn't exist anymore.

If you return a function from your effect React will save it and call it if the component is removed from the page. React will also run it to clean up when a component re-renders (before the effects run again).

Let's clean up after our effect example from above:

function Counter(props) {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    function handleKeyDown(event) {
      if (event.key === "ArrowUp") {
        setCount((prevCount) => prevCount + 1);
      }
    }
    // run handler function when keydowns happen
    window.addEventListener("keydown", handleKeyDown);
    // create fn that removes event listener
    function cleanup() {
      window.removeEventListener("keydown", handleKeyDown);
    }
    // react will run `cleanup` whenever it needs to remove this effect
    return cleanup;
  }, []);

  return <div>Count is {count}</div>;
}

The cleanup function we return will be called if the component unmounts (is removed from the page). That will ensure we don't keep running an unnecessary event listener and trying to update state that doesn't exist anymore.

Challenge 2

We're going to enhance our Toggle component from Part 3. You can either keep working in the same file or open up challenge-2.html to start fresh.

  1. Edit the Toggle component so that the page title (in the tab) shows whether the toggle is on or off.

  2. Then create a new component called MousePosition. It should keep track of where the mouse is in the window and render the mouse x and y positions.

  3. Put MousePosition inside your Toggle so you can show and hide it. This is how your final App should look:

    function App() {
      return (
        <Toggle>
          <MousePosition />
        </Toggle>
      );
    }
Previousreact-state-effectsNextreal-world-fetch

Last updated 2 years ago

counter-example
toggle-example
effect-example
effect-example