Home > other >  Converting a React Class Component to a Function Component
Converting a React Class Component to a Function Component

Time:04-29

I've been trying to convert the following code from React Class Component to Function Component but I've been having problems since I've gotten the error "Expected an assignment or function call and instead saw an expression. eslint no-unused-expressions"

componentDidMount() {
    this.startingSequence();
}


startingSequence = () => {
    setTimeout(() => {
      this.setState(
        () => {
          return {
            textMessageOne: `A wild ${this.state.enemyName} appeared!`,
            enemyFaint: false
          };
        },
        () => {
          setTimeout(() => {
            this.setState(
              {
                textMessageOne: `Go ${this.state.playerName}!`,
                playerFaint: false
              },
              () => {
                setTimeout(() => {
                  this.setState({
                    textMessageOne: ""
                  });
                }, 3000);
              }
            );
          }, 3000);
        }
      );
    }, 1000);
  };

This is the code I ended up with while trying to convert it to Function Component:

const startingSequence = () => {
        setTimeout(() => {
            () => {
                setTextMessageOne(state => {
                    state = (`Wild ${enemyName} appeared!`)
                    return state;})
                setEnemyFaint(state => {
                    state = false
                    return state;})
            }
            ,
            () => {
                setTimeout(() => {
                    setTextMessageOne(`Go ${playerName}!`),
                    setPlayerFaint(false)
                    , 
                    () => {
                        setTimeout(() => {
                            setTextMessageOne("")
                        }, 3000);
                    }
                }, 3000);
            }
        }, 1000);
    };

useEffect(() => { 
    startingSequence(); 
}) 

EDIT: Solution I got thanks to Kieran Osgood:

    const startingSequence = () => {
        setTimeout(() => {
          setTextMessageOne(`Wild ${enemyName} appeared!`)
          setEnemyFaint(false)
          setTimeout(() => {
            setTextMessageOne(`Go ${playerName}!`)
            setPlayerFaint(false)
            setTimeout(() => {
            setTextMessageOne('')
            }, 3000)
        }, 3000)
        }, 1000)
    }
    
    useEffect(() => {
        startingSequence()
    }, [enemyFaint])

CodePudding user response:

In the functional component syntax you can pass the new state in directly OR use the function syntax if you need access to the previous state, however the state variable is not assignable so when you're doing this:

setTextMessageOne(state => {
    state = `Wild ${enemyName} appeared!`
    return state
})

You could do it simply like this:

setTextMessageOne(`Wild ${enemyName} appeared!`)

Function syntax is helpful for lets say a counter, where we're incrementing a number, and avoids getting stale closures overlapping each other.

setCounter(previousState => {
    return previousState   1
})
// OR
setCounter(previousState => previousState   1)

So amending that, the other issue is theres a lot of nested arrow functions which seem to stem from the previous usage of the second argument to setState which is a callback to be executed immediately after the state is set - this doesn't exist in functional components, so you should probably refactor this function to be something more along the lines of

// this is just a basic representation, consider combining these to objects etc.
  const [enemyName, setEnemyName] = React.useState('')
  const [enemyFaint, setEnemyFaint] = React.useState(false)
  const [playerFaint, setPlayerFaint] = React.useState(false)
  const [textMessageOne, setTextMessageOne] = React.useState('')
  const [playerName, setPlayerName] = React.useState('')

  const startingSequence = () => {
    setTimeout(() => {
      setTextMessageOne(state => {
        state = `Wild ${enemyName} appeared!`
        return state
      })
      setEnemyFaint(false)
    }, 1000)
  }

  React.useEffect(() => {
    setTimeout(() => {
      setTextMessageOne(`Go ${playerName}!`)
      setPlayerFaint(false)

      setTimeout(() => {
        setTextMessageOne('')
      }, 3000)
    }, 3000)
  }, [enemyFaint])

Then you want to take these further to extract into custom hooks so its more clear your intent in the flow of your component but generally this is the way in functional components to respond to state changes, via the useEffect

  • Related