Skip to content

Exercises

Fix the violation

Below, we have a "Search" UI with a conditionally-rendered text input. Clicking the “” icon slides a text input into place:

The code below is violating the Rules of Hooks! Your mission is to update the code to comply with the rules.

Acceptance Criteria:

  • No lint warnings should be shown.
  • Clicking the search button should reveal a search input.

Code Playground

import React from 'react';
import { X, Search } from 'react-feather';

import VisuallyHidden from './VisuallyHidden';

function App() {
const [
showSearchField,
setShowSearchField,
] = React.useState(false);

let searchId;
if (showSearchField) {
searchId = React.useId();
}

function handleToggleSearch(event) {
event.preventDefault();
setShowSearchField(!showSearchField);
}

return (
<>
<form>
{showSearchField && (
<div className="search-field-wrapper">
<label
htmlFor={searchId}
>
<VisuallyHidden>
Search
</VisuallyHidden>
</label>
<input
id={searchId}
className="search-field"
/>
</div>
)}
<button
className="search-toggle-button"
onClick={handleToggleSearch}
>
{showSearchField ? <X /> : <Search />}
<VisuallyHidden>
Toggle search field
</VisuallyHidden>
</button>
</form>
</>
);
}

export default App;

Lint Warning

  • React Hook "React.useId" is called conditionally. React Hooks must be called in the exact same order in every component render.

    Rule: react-hooks/rules-of-hooks

    Location: Line 14, Column 16

Solution:

Fix another violation

Let's do it one more time, with a different scenario!

In this exercise, our LoginForm component can either render:

  • A form including email/password fields, if the user isn't logged in
  • A paragraph, if the user is logged in

You can toggle between these states by submitting the form.

Unfortunately, we're getting a lint warning! Your mission is to figure out what's causing it, and to fix it.

Acceptance Criteria:

  • Update the code below so that it doesn't violate the Rules of Hooks

Code Playground

import React from 'react';

function LoginForm({ isLoggedIn, handleLogin }) {
const id = React.useId();

if (isLoggedIn) {
return <p>You're already logged in!</p>;
}

const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');

return (
<form onSubmit={handleLogin}>
<div className="row">
<label htmlFor={`${id}-email`}>Email:</label>
<input
type="email"
id={`${id}-email`}
value={email}
onChange={(event) => {
setEmail(event.target.value);
}}
/>
</div>
<div className="row">
<label htmlFor={`${id}-password`}>
Password:
</label>
<input
type="password"
id={`${id}-password`}
value={password}
onChange={(event) => {
setPassword(event.target.value);
}}
/>
</div>
<button>Log in</button>
</form>
);
}

export default LoginForm;

Lint Warning

  • React Hook "React.useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

    Rule: react-hooks/rules-of-hooks

    Location: Line 10, Column 29

  • React Hook "React.useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

    Rule: react-hooks/rules-of-hooks

    Location: Line 11, Column 35

Solution: