mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
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:
@@ -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"
|
||||
}
|
||||
+115
@@ -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--
|
||||
```
|
||||
+127
@@ -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--
|
||||
```
|
||||
+127
@@ -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--
|
||||
```
|
||||
+151
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+144
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+118
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+120
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+114
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+124
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+124
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+122
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+165
@@ -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>
|
||||
);
|
||||
};
|
||||
```
|
||||
+262
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user