Phase 1: App Component

Understanding the Problem

In this phase, we'll refactor the App class component to a function component. This is an excellent starting point as it's relatively simple but introduces the fundamental concept of state management with hooks.

The key aspects we need to address in the App component are:

The App component is the root component of our application. It's responsible for rendering all the other widget components and controlling the visibility of the Clock component through a toggle button.

George Polya's Problem-Solving Method

Step 1: Understand the Problem

Let's first look at the original App class component to understand its functionality:


// App.jsx
import React from 'react';
import Clock, { ClockToggle } from './components/Clock';
import Folder from './components/Folder';
import Weather from './components/Weather';
import Autocomplete from './components/Autocomplete';

const names = [
  'Abba',
  'Barbara',
  'Barney',
  'Jeff',
  'Jenny',
  'Sally',
  'Sarah',
  'Xander'
];

const folders = [
  { title: 'one', content: 'I am the first' },
  { title: 'two', content: 'Second folder here' },
  { title: 'three', content: 'Third folder here' }
];

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showClock: true
    };
  }
  
  toggleClock = () => this.setState({ showClock: !this.state.showClock });
  
  render () {
    return (
      <div className="widgets">
        <Folder folders={folders} />
        <Weather />
        <ClockToggle toggleClock={this.toggleClock} />
        {this.state.showClock && <Clock />}
        <Autocomplete names={names} />
      </div>
    );
  }
}

export default App;
            

Examining this code, we can see that:

Step 2: Devise a Plan

  1. Change the class declaration to a function declaration
  2. Replace constructor and this.state with the useState hook
  3. Convert the toggleClock method to a regular function
  4. Remove the render() method and directly return JSX
  5. Update any references to this.state or this.toggleClock

Step 3: Carry Out the Plan

Let's implement our plan step by step:

1. Change the Component Declaration

We'll change from a class that extends React.Component to a regular JavaScript function:


// From
class App extends React.Component {
  // ...
}

// To
function App() {
  // ...
}
            

2. Import useState and Replace State Management

We need to import the useState hook and use it to replace the class state:


// Add this import
import React, { useState } from 'react';

// Replace constructor and this.state with useState
const [showClock, setShowClock] = useState(true);
            

The useState hook takes an initial value (true in this case) and returns an array with two elements: the current state value and a function to update it. We're using array destructuring to assign these to variables.

3. Convert the toggleClock Method

We'll convert the class method to a regular function and use the setShowClock function:


// From
toggleClock = () => this.setState({ showClock: !this.state.showClock });

// To
const toggleClock = () => setShowClock(!showClock);
            

4. Update JSX References

Now we need to update references in the returned JSX:


// From
<ClockToggle toggleClock={this.toggleClock} />
{this.state.showClock && <Clock />}

// To
<ClockToggle toggleClock={toggleClock} />
{showClock && <Clock />}
            

5. Remove the render Method

Finally, we'll remove the render() method and return the JSX directly from the function component:


// From
render () {
  return (
    <div className="widgets">
      // ...
    </div>
  );
}

// To
return (
  <div className="widgets">
    // ...
  </div>
);
            

Putting it all together, our refactored component looks like this:


// App.jsx (Refactored)
import React, { useState } from 'react';
import Clock, { ClockToggle } from './components/Clock';
import Folder from './components/Folder';
import Weather from './components/Weather';
import Autocomplete from './components/Autocomplete';

const names = [
  'Abba',
  'Barbara',
  'Barney',
  'Jeff',
  'Jenny',
  'Sally',
  'Sarah',
  'Xander'
];

const folders = [
  { title: 'one', content: 'I am the first' },
  { title: 'two', content: 'Second folder here' },
  { title: 'three', content: 'Third folder here' }
];

function App() {
  // Replace this.state with useState
  const [showClock, setShowClock] = useState(true);
  
  // Convert toggleClock method to a regular function
  const toggleClock = () => setShowClock(!showClock);
  
  // Return what was previously in the render method
  return (
    <div className="widgets">
      <Folder folders={folders} />
      <Weather />
      <ClockToggle toggleClock={toggleClock} />
      {showClock && <Clock />}
      <Autocomplete names={names} />
    </div>
  );
}

export default App;
            

Step 4: Look Back and Review

Let's verify that our refactored component maintains the same functionality as the original:

Run the tests to make sure everything is working as expected:

npm test

All tests for the App component should pass, confirming that our refactoring was successful.

Deep Dive: Understanding useState

Let's take a closer look at the useState hook, which is central to state management in function components.

How useState Works

The useState hook is a function that you call inside your component to add state to it. It returns a pair of values: the current state and a function that updates it.

const [state, setState] = useState(initialValue);

When you call useState, you're telling React that:

  1. You want this component to remember something (the state)
  2. The initial value of that state is what you passed as the argument
  3. You'll use the first element of the returned array to read the state
  4. You'll use the second element of the returned array to change the state

Class Component vs. Function Component State

Let's compare how state is handled in both approaches:

Class Component:

Function Component with useState:

Advantages of useState

The useState approach offers several advantages:

Real-World Applications

This pattern of replacing class state with useState is extremely common in React applications. As you migrate legacy React code or build new features, you'll frequently use this technique to manage component state.

Some examples of where you might use useState in real applications: