Arpit Falcon
Arpit Falcon

Reputation: 234

How to replace class-based components with hooks in react

I went to the official reactjs website and found this

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

If i am replacing

componentDidMount() {
        window.addEventListener('keydown', this.keypressed);
    }

with this

useEffect(() => {
        window.addEventListener('keydown', keypressed);
    },[]);

it should be an issue, right?

But when I am using useEffect and pressing the key very fast then it's breaking the site which is not happening in the class-based components.

This is my class-based component code which is working fine and not breaking the site

/* eslint-disable eqeqeq */
import React, { Component } from 'react';
import data from './content/data';
import List from './List';
import Image from './Image';
import '../style/App.sass';

export class App extends Component {
    state = {
        currentIndex: 0,
    };

    componentDidMount() {
        window.addEventListener('keydown', this.keypressed);
    }

    keypressed = (e) => {
        let { currentIndex } = this.state;
        if (e.keyCode == '38' || e.keyCode == '40') e.preventDefault();
        if (e.keyCode == '38') {
            this.setState({
                currentIndex: (currentIndex - 1 + data.length) % data.length,
            });
        }
        if (e.keyCode == '40') {
            this.setState({
                currentIndex: (currentIndex + 1) % data.length,
            });
        }
    };

    sendIndex = (i) =>
        this.setState({
            currentIndex: i,
        });

    render() {
        return (
            <div className="container0">
                <List
                    currentIndex={this.state.currentIndex}
                    sendIndex={this.sendIndex}
                />
                <Image currentIndex={this.state.currentIndex} />
            </div>
        );
    }
}

export default App;

This is my function-based component code

/* eslint-disable eqeqeq */
import React, { useState, useEffect } from 'react';
import data from './content/data';
import List from './List';
import Image from './Image';
import '../style/App.sass';

const App = () => {
    const [currentIndex, setCurrentIndex] = useState(0);

    const keypressed = (e) => {
        if (e.keyCode == '38' || e.keyCode == '40') e.preventDefault();

        if (e.keyCode == '38')
            setCurrentIndex((currentIndex - 1 + data.length) % data.length);

        if (e.keyCode == '40')
            setCurrentIndex((currentIndex + 1) % data.length);
    };

    const sendIndex = (i) => setCurrentIndex(i);

    // window.addEventListener('keydown', keypressed);

    useEffect(() => {
        window.addEventListener('keydown', keypressed);
    }, []);

    return (
        <div className="container0">
            <List currentIndex={currentIndex} sendIndex={sendIndex} />
            <Image currentIndex={currentIndex} />
        </div>
    );
};

export default App;

Please tell me what I am doing wrong.

Upvotes: 1

Views: 235

Answers (1)

Dennis Vash
Dennis Vash

Reputation: 53914

For unknown reason you disabled eslint errors (/* eslint-disable */), with them you would be suggested to fix the closure on staled value of currentIndex state.

Possible fix is passing a callback to useState setter:

const App = () => {
  const [currentIndex, setCurrentIndex] = useState(0);
  const sendIndex = (i) => setCurrentIndex(i);

  useEffect(() => {
    const keypressed = (e) => {
      if (e.keyCode == "38" || e.keyCode == "40") e.preventDefault();

      if (e.keyCode == "38")
        setCurrentIndex(
          (prevIndex) => (prevIndex - 1 + data.length) % data.length
        );

      if (e.keyCode == "40")
        setCurrentIndex((prevIndex) => (prevIndex + 1) % data.length);
    };
    window.addEventListener("keydown", keypressed);
  }, []);

  return (
    <div className="container0">
      <List currentIndex={currentIndex} sendIndex={sendIndex} />
      <Image currentIndex={currentIndex} />
    </div>
  );
};

Upvotes: 2

Related Questions