feat(curriculum): add toggle visibility useState workshop (#59193)

Co-authored-by: Naomi <accounts+github@nhcarrigan.com>
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
Jessica Wilkins
2025-04-29 16:50:32 -07:00
committed by GitHub
parent 05b1117519
commit cd09f71590
17 changed files with 1894 additions and 0 deletions
+6
View File
@@ -3485,6 +3485,12 @@
"In these lecture videos, you will learn about working with state and responding to events with React."
]
},
"workshop-toggle-text-app": {
"title": "Toggle Text App",
"intro": [
"In this workshop, you will continute to learn about the <code>useState()</code> hook by building an application that hides and shows a piece of text on the screen."
]
},
"lab-color-picker": {
"title": "Build a Color Picker App",
"intro": [
@@ -0,0 +1,9 @@
---
title: Introduction to the Toggle Text App
block: workshop-toggle-text-app
superBlock: full-stack-developer
---
## Introduction to the Toggle Text App
This workshop will cover the `useState` hook.
@@ -0,0 +1,65 @@
{
"name": "Toggle Text App",
"blockType": "workshop",
"blockLayout": "challenge-grid",
"isUpcomingChange": true,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "workshop-toggle-text-app",
"superBlock": "full-stack-developer",
"challengeOrder": [
{
"id": "67cd33334277c108f5e0cb21",
"title": "Step 1"
},
{
"id": "67cd45eb92c40b176d987d10",
"title": "Step 2"
},
{
"id": "67cd46ec4ec904186ad52ba5",
"title": "Step 3"
},
{
"id": "67cd49e05693111a41771f29",
"title": "Step 4"
},
{
"id": "67cd4cf8b5fe391b324ad11f",
"title": "Step 5"
},
{
"id": "67cd4fe67813f71c3479e64a",
"title": "Step 6"
},
{
"id": "67cd50e3d32a311cfced03e2",
"title": "Step 7"
},
{
"id": "67cd51b36a4e711da29a8622",
"title": "Step 8"
},
{
"id": "67cd525b0e575b1e5abf4c03",
"title": "Step 9"
},
{
"id": "67cd57e96257ed1fabbe4879",
"title": "Step 10"
},
{
"id": "67cd59f6581d3a20c9cc6460",
"title": "Step 11"
},
{
"id": "67cd5bace629fa219a70aa6d",
"title": "Step 12"
},
{
"id": "67cd61c24bfc0a240132fd2f",
"title": "Step 13"
}
],
"helpCategory": "JavaScript"
}
@@ -0,0 +1,115 @@
---
id: 67cd33334277c108f5e0cb21
title: Step 1
challengeType: 0
dashedName: step-1
demoType: onLoad
---
# --description--
In the previous lecture videos, you learned about state and how to work with the `useState` hook. For this workshop, you will create an app that will hide/show a piece of text on the screen when a user clicks on a button.
All of the boilerplate code, including the CSS, has been provided for you.
To begin, return a `div` element inside of the `ToggleApp` component with an `id` called `toggle-container`.
# --hints--
You should have a `div` element.
```js
assert.exists(document.querySelector("div"));
```
Your `div` element should have an `id` called `toggle-container`.
```js
const el = document.getElementById("toggle-container");
assert.exists(el);
assert.strictEqual(el.tagName, "DIV");
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
--fcc-editable-region--
export const ToggleApp = () => {
};
--fcc-editable-region--
```
@@ -0,0 +1,127 @@
---
id: 67cd45eb92c40b176d987d10
title: Step 2
challengeType: 0
dashedName: step-2
---
# --description--
Inside the `toggle-container` element, add a `button` element with an `id` called `toggle-button`. The button text should say `Message`.
# --hints--
You should have a `button` element.
```js
assert.exists(document.querySelector("button"));
```
Your `button` element should have an `id` called `toggle-button`.
```js
const buttonEl = document.querySelector("button");
assert.exists(document.getElementById("toggle-button"))
```
Your button text should be `Message`.
```js
const buttonEl = document.querySelector("button");
assert.equal(buttonEl.textContent, "Message");
```
You `button` element should be inside of the `toggle-container` element.
```js
const buttonEl = document.querySelector("#toggle-container > button");
assert.exists(buttonEl);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
--fcc-editable-region--
export const ToggleApp = () => {
return (
<div id="toggle-container">
</div>
);
};
--fcc-editable-region--
```
@@ -0,0 +1,127 @@
---
id: 67cd46ec4ec904186ad52ba5
title: Step 3
challengeType: 0
dashedName: step-3
---
# --description--
Below your `button` element, add a paragraph element with an `id` of `message` and the text `I love freeCodeCamp!`.
# --hints--
You should have a paragraph element.
```js
assert.exists(document.querySelector("p"));
```
Your paragraph element should have an `id` of `message`.
```js
assert.exists(document.getElementById("message"))
```
Your paragraph element should have the text `I love freeCodeCamp!`.
```js
const paraEl = document.querySelector("p");
assert.equal(paraEl?.textContent, "I love freeCodeCamp!");
```
Your paragraph element should be below the `button` element.
```js
const paraEl = document.querySelector("button + p");
assert.exists(paraEl);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
--fcc-editable-region--
export const ToggleApp = () => {
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
</div>
);
};
--fcc-editable-region--
```
@@ -0,0 +1,151 @@
---
id: 67cd49e05693111a41771f29
title: Step 4
challengeType: 0
dashedName: step-4
---
# --description--
In the prior lecture videos, you learned how to create state variables by using the `useState` hook like this:
```js
const [user, setUser] = useState("Jessica");
```
The convention for naming state variables is to use the `[example, setExample]` format, utilizing array destructuring.
`useState` returns an array of two values. The first value is the current state. The second value is the set function that will be used to update state.
Create a new state variable and set function called `isVisible` and `setIsVisible`. Remember to use the array destructuring syntax as shown in the example.
Then assign the `useState` hook with an initial value of `false`.
# --hints--
You should use the array destructuring syntax for the `isVisible` and `setIsVisible` variables. Refer back to the example.
```js
assert.match(code, /(const|let)\s+\[\s*(isVisible)\s*,\s*(setIsVisible)\s*\]\s*/);
```
You should use the `useState` hook.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const _b = await __helpers.prepTestComponent(exports.ToggleApp);
assert.isAtLeast(abuseState.calls.length, 1);
}
```
Your `useState` hook should have an initial value of `false`.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const _b = await __helpers.prepTestComponent(exports.ToggleApp);
assert.equal(abuseState.calls[0]?.[0], false);
}
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
--fcc-editable-region--
--fcc-editable-region--
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
<p id="message">I love freeCodeCamp!</p>
</div>
);
};
```
@@ -0,0 +1,144 @@
---
id: 67cd4cf8b5fe391b324ad11f
title: Step 5
challengeType: 0
dashedName: step-5
---
# --description--
Now that you have created the `isVisible` state variable, it is time to start using it.
In prior lecture videos, you learned about inline conditional rendering which allows you to show different content based on a certain condition. It is a common pattern to use the logical AND (`&&`) operator to conditionally render a piece of text like this:
```js
function Notification({ message }) {
return (
<div>
{message && <p>{message}</p>}
</div>
);
}
```
In this example, the paragraph element will only show if the message is truthy. Remember that "truthy" refers to any value that evaluates to `true` in a Boolean context.
For this step, you will need to conditionally render the paragraph element. When done correctly, you should see that the paragraph is no longer visible on the page.
# --hints--
You should conditionally render the paragraph so it only shows when `isVisible` is `true`. Refer back to the example if you need help.
```js
async () => {
{
React.useState = (arg) => ([false, () => {}]);
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const div = await __helpers.prepTestComponent(exports.ToggleApp);
const pEl = div.querySelector("p#message");
assert.notExists(pEl);
}
{
React.useState = (arg) => ([true, () => {}]);
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const div = await __helpers.prepTestComponent(exports.ToggleApp);
const pEl = div.querySelector("p#message");
assert.exists(pEl);
}
}
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
--fcc-editable-region--
<p id="message">I love freeCodeCamp!</p>
--fcc-editable-region--
</div>
);
};
```
@@ -0,0 +1,118 @@
---
id: 67cd4fe67813f71c3479e64a
title: Step 6
challengeType: 0
dashedName: step-6
---
# --description--
One way to show the paragraph text is to change the initial value for the `useState` hook.
Change the current `useState` value from `false` to `true` and you should see that the paragraph text is now displaying on the screen.
# --hints--
You should change the initial value for `useState` to `true`.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const _b = await __helpers.prepTestComponent(exports.ToggleApp);
assert.equal(abuseState.calls[0]?.[0], true);
}
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
--fcc-editable-region--
const [isVisible, setIsVisible] = useState(false);
--fcc-editable-region--
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,120 @@
---
id: 67cd50e3d32a311cfced03e2
title: Step 7
challengeType: 0
dashedName: step-7
---
# --description--
While that works, it is not very practical to manually update the initial value for the `useState` hook to toggle the visibility for the paragraph text.
Update the initial value for the `useState` hook from `true` to `false`.
In the next few steps, you will build out the button functionality that will handle the toggle visibility for the paragraph text.
# --hints--
You should set the initial value for the `useState` hook back to `false`.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText;
const exports = {};
const _a = eval(script);
const _b = await __helpers.prepTestComponent(exports.ToggleApp);
assert.equal(abuseState.calls[0]?.[0], false);
}
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
--fcc-editable-region--
const [isVisible, setIsVisible] = useState(true);
--fcc-editable-region--
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,114 @@
---
id: 67cd51b36a4e711da29a8622
title: Step 8
challengeType: 0
dashedName: step-8
---
# --description--
To toggle the visibility for the text, you will need to first create a function that will be responsible for that behavior.
Start by creating a new function called `handleToggleVisibility`.
# --hints--
You should have a `handleToggleVisibility` function.
```js
// Tried using isDefined and isFunction here but it didn't work. assert.match works though
assert.match(code, /function\s+handleToggleVisibility\(\)\s*\{[\s\S]*\}|(const|var|let)\s+handleToggleVisibility\s*=\s*\(\)\s*=>\s*\{[\s\S]*\}/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
--fcc-editable-region--
--fcc-editable-region--
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,124 @@
---
id: 67cd525b0e575b1e5abf4c03
title: Step 9
challengeType: 0
dashedName: step-9
---
# --description--
As mentioned earlier, the set function is responsible for updating state. A common approach when toggling between boolean state values is to update the state in this way:
```js
const [isUserLoggedIn, setIsUserLoggedIn] = useState(false);
// updating state
setIsUserLoggedIn(!isUserLoggedIn);
```
The following example is using the set function called `setIsUserLoggedIn` to update the login status for the user. It is common practice to use the logical NOT (`!`) operator to toggle between `true` and `false`. Since the initial value is `false`, applying the `!` operator will change it to `true`.
Inside of the `handleToggleVisibility` function, use `setIsVisible` to update the visibility state using the logical NOT (`!`) operator.
# --hints--
You should use `setIsVisible` to update the visibility for the text. Refer to the example if you need help.
```js
assert.match(code, /setIsVisible\s*\(\s*!isVisible\s*\)/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
--fcc-editable-region--
const handleToggleVisibility = () => {
}
--fcc-editable-region--
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,124 @@
---
id: 67cd57e96257ed1fabbe4879
title: Step 10
challengeType: 0
dashedName: step-10
---
# --description--
**NOTE**: A `useEffect` hook was added to call the `handleToggleVisibility` function when the component renders. You will learn more about the `useEffect` hook in a future lecture and workshop.
Now would be a good time to see the updated `isVisible` value.
Below the `setIsVisible(!isVisible);`, log the `isVisible` state variable to the console.
Open up the console and you will notice that the value is not what you expected it to be.
# --hints--
You should use `console.log` to log the `isVisible` state variable to the console.
```js
assert.match(code, /console\.log\(\s*isVisible\s*\)/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState, useEffect } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
--fcc-editable-region--
const handleToggleVisibility = () => {
setIsVisible(!isVisible);
}
--fcc-editable-region--
useEffect(() => {
handleToggleVisibility();
}, [])
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,122 @@
---
id: 67cd59f6581d3a20c9cc6460
title: Step 11
challengeType: 0
dashedName: step-11
---
# --description--
If you looked at the console, you might have been surprised to see the value of `false` instead of `true`. This is happening because React doesn't immediately update state. The state will only be updated on the next render cycle.
It is a common mistake for developers new to React to place console statements right after a set function. So this is something to be aware when you are building out your React applications.
Since the `console.log()` is no longer needed, remove it from your `handleToggleVisibility` function.
# --hints--
You should no longer have the `console.log(isVisible);` in your code.
```js
assert.notMatch(code, /console\.log\s*\(\s*isVisible\s*\)\s*;?/);
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState, useEffect } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
--fcc-editable-region--
const handleToggleVisibility = () => {
setIsVisible(!isVisible);
console.log(isVisible);
}
--fcc-editable-region--
useEffect(() => {
handleToggleVisibility();
}, [])
return (
<div id="toggle-container">
<button id="toggle-button">Message</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,165 @@
---
id: 67cd5bace629fa219a70aa6d
title: Step 12
challengeType: 0
dashedName: step-12
---
# --description--
Now it is time to use your `handleToggleVisibility` function.
In prior lecture videos, you learned about React's Synthetic Event System and how to work with attributes like `onClick` and `onSubmit`.
Remember that you can pass a function reference to an `onClick` attribute like this:
```jsx
function handleClick() {
console.log("Button clicked!");
}
<button onClick={handleClick}>Click Me</button>;
```
Update your existing `button` element to add an `onClick` attribute and pass in a `handleToggleVisibility` function reference.
Now, when you click on the button, you will see the visibility of the paragraph text toggle between hidden and shown on the page.
# --hints--
When the `toggle-button` is clicked, the message `"I love freeCodeCamp!"` should appear on the screen.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText.replace("_React.useState", "abuseState");
const exports = {};
const a = eval(script);
const s = await __helpers.prepTestComponent(exports.ToggleApp);
const btn = s.querySelector('#toggle-button');
assert.exists(btn);
await React.act(async () => {
const ev = new MouseEvent("click", { bubbles: true, cancelable: true });
btn.dispatchEvent(ev);
});
const alteredStates = abuseState.returns;
const initialStates = alteredStates.splice(0, abuseState.returns.length / 2);
const stateChanged = initialStates.some((s, i) => {
try {
assert.deepEqual(s[0], alteredStates[i][0]);
return false;
} catch (e) {
return true;
}
});
const msg = s.querySelector("#message");
assert.exists(msg)
assert.equal(msg.textContent, "I love freeCodeCamp!");
abuseState.restore();
assert.isTrue(stateChanged);
};
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
const handleToggleVisibility = () => {
setIsVisible(!isVisible);
}
return (
<div id="toggle-container">
--fcc-editable-region--
<button id="toggle-button">Message</button>
--fcc-editable-region--
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -0,0 +1,262 @@
---
id: 67cd61c24bfc0a240132fd2f
title: Step 13
challengeType: 0
dashedName: step-13
---
# --description--
It would be nice to dynamically show the words `Show Message` or `Hide Message` based on the `isVisible` state.
Earlier, you reviewed how to work with inline conditional rendering. You are going to apply similar logic to the button text.
Here is an example of using the ternary operator to conditionally show the words `Hide` and `Show`:
```jsx
<button>{isMenuVisible? "Hide" : "Show"} Menu</button>
```
For the last step of the workshop, use the ternary operator to conditionally display the words `Hide` or `Show` for the message button. Place the condition before the `Message` text so it displays `Show Message` or `Hide Message`.
And with that last change, your workshop is complete! Test your button to see it in action.
# --hints--
When `isVisible` is `false`, the button text should say `Show Message`.
```js
const btn = document.getElementById("toggle-button");
assert.equal(btn.textContent, "Show Message");
```
When `isVisible` is `true`, the button text should say `Hide Message`.
```js
async () => {
const abuseState = __helpers.spyOn(React, "useState");
const script = [...document.querySelectorAll("script")].find((s) => s.dataset.src === "index.jsx").innerText.replace("_React.useState", "abuseState");
const exports = {};
const a = eval(script);
const s = await __helpers.prepTestComponent(exports.ToggleApp);
const btn = s.querySelector('#toggle-button');
assert.exists(btn);
assert.equal(btn.textContent, "Show Message");
await React.act(async () => {
const ev = new MouseEvent("click", { bubbles: true, cancelable: true });
btn.dispatchEvent(ev);
});
const alteredStates = abuseState.returns;
const initialStates = alteredStates.splice(0, abuseState.returns.length / 2);
const stateChanged = initialStates.some((s, i) => {
try {
assert.deepEqual(s[0], alteredStates[i][0]);
return false;
} catch (e) {
return true;
}
});
assert.equal(btn.textContent, "Hide Message");
abuseState.restore();
assert.isTrue(stateChanged);
};
```
# --seed--
## --seed-contents--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
const handleToggleVisibility = () => {
setIsVisible(!isVisible);
}
return (
<div id="toggle-container">
--fcc-editable-region--
<button onClick={handleToggleVisibility} id="toggle-button">Message</button>
--fcc-editable-region--
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
# --solutions--
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Toggle Visibility</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.jsx"
></script>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { ToggleApp } from "./index.jsx";
ReactDOM.createRoot(document.getElementById("root")).render(<ToggleApp />);
</script>
</body>
</html>
```
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
#toggle-container {
text-align: center;
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#toggle-button {
padding: 10px 20px;
border: none;
background: #007BFF;
color: white;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s ease;
}
#toggle-button:hover {
background: #0056b3;
}
#message {
margin-top: 20px;
font-size: 1.125rem;
color: #333;
}
```
```jsx
const { useState } = React;
export const ToggleApp = () => {
const [isVisible, setIsVisible] = useState(false);
const handleToggleVisibility = () => {
setIsVisible(!isVisible);
}
return (
<div id="toggle-container">
<button onClick={handleToggleVisibility} id="toggle-button">
{isVisible ? "Hide" : "Show"} Message
</button>
{isVisible && <p id="message">I love freeCodeCamp!</p>}
</div>
);
};
```
@@ -1072,6 +1072,7 @@
{
"dashedName": "lecture-working-with-state-and-responding-to-events-in-react"
},
{ "dashedName": "workshop-toggle-text-app" },
{
"dashedName": "lecture-understanding-effects-and-referencing-values-in-react"
},