Search code examples
reactjsreact-hooksreact-class-based-component

Can't convert class to function component, "unmounted error"


Quick foreword: If you are rushing new posts, please move on instead of marking this as a duplicate as I really need help. Apologies if I sound harsh but I've been through this too many times.

I am facing trouble converting the following component code into function-based code. Can anyone please help?

class App extends React.Component {
  state = {loadings: [],};

  enterLoading = index => {
    this.setState(({ loadings }) => {
      const newLoadings = [...loadings];
      newLoadings[index] = true;
      return {loadings: newLoadings,};
    });
    setTimeout(() => {
      this.setState(({ loadings }) => {
        const newLoadings = [...loadings];
        newLoadings[index] = false;
        return {loadings: newLoadings,};
      });
    }, 6000);
  };

  render() {
    const { loadings } = this.state;
    return (
      <>
        <Button loading={loadings[1]} onClick={() => this.enterLoading(1)}>
          Click me!
        </Button>
      </>
    );
  }
}

ReactDOM.render(<App />, mountNode);

Here's what I tried:

const [loadings, setloadings] = useState([]);

useEffect((index) => {
  console.log("here");
  const newLoadings = [...loadings];
  newLoadings[index] = true;
  setloadings(newLoadings);

  setTimeout(() => {
    const newLoadings = [...loadings];
    newLoadings[index] = false;
    setloadings(newLoadings);
  }, 6000);
}, []);

// blah blah blah

<Button loading={loadings[0]} onClick={() => setloadings(0)}>Log in</Button>

Essentially I bounced between different convert-class-to-funtion tutorials came up with a problem as described in this thread. I then tried to implement my own version -- and it didn't work.

FYI, "here" was printed in the console, and the console didn't throw up any errors -- the button just wouldn't animate.

Many thanks in advance.


Solution

  • My initial though is you’re expecting loadings to be an already initialized array in your return. Have you tried wrapping the JSX return with loadings.length &&?

    The other thought is the useEffect is slated to happen on initial mount and doesn’t have the context yet of loadings. Maybe changing the context to [loadings] might solve that.

    EDIT: to handle conditional logic to keep this from running unless action is taken, you could do:

    useEffect((index) => {
        if (loadings[index] !== true) {
          console.log("here");
          const newLoadings = [...loadings];
          newLoadings[index] = true;
          setloadings(newLoadings);
    
          setTimeout(() => {
            const newLoadings = [...loadings];
            newLoadings[index] = false;
            setloadings(newLoadings);
          }, 6000);
        }
    }, [loadings]);