MuZak
MuZak

Reputation: 301

React child component don't update after parent state changed

This question has been asked several times but I can find answers only for class component and not functional components, and my issue seems a bit weird to me so I am interested in why React behaves like this.

I want to create a timer in React Native.

When the timer ends, it triggers the onTimeEnd method, that updates the state of the App component and triggers the useEffect on the timeInSec prop of the Timer component.

When the reset button of the App component is pressed, it updates the state of the App component but does not trigger the useEffect the Timer component. I can't understand why.

I hope you will see what I mean with my code (I removed other functionalities that I believe do not affect this code).

App component:

import React, { useState } from 'react'
import { TouchableOpacity, StyleSheet, Text, View, TextInput } from 'react-native'

import Layout from './components/Layout'
import Timer from './components/Timer'

const App = () => {
    const defaultTime = 20

    const [timeLeft, setTimeLeft] = useState(defaultTime)

    const onTimeEnd = () => {
        setTimeLeft(defaultTime)
    }

    const resetTimer = () => {
        setTimeLeft(defaultTime)
    }

    return (
        <Timer
            timeInSec={timeLeft}
            onTimeEnd={onTimeEnd}
        />
        <TouchableOpacity onPress={resetTimer}>
            <Text>Reset</Text>
        </TouchableOpacity>
    );
}

export default App

Timer component:

import React, { useEffect, useState } from 'react'
import { View, Text} from 'react-native'

const Timer = (props) => {
    const [timeInSec, setTimeInSec] = useState(props.timeInSec)

    useEffect(() => {
        const interval = setInterval(() => {
            const newTime = timeInSec - 1
            setTimeInSec(newTime)

            if (newTime === 0) {
                clearInterval(interval)
                props.onTimeEnd()
            }
        }, 1000)

        return () => clearInterval(interval)
    })

    useEffect(() => {
        // This useEffect is triggered when the state of the App component changes from the onTimeEnd function,
        // but not when it changes from the resetTimer function. Why ?
        setTimeInSec(props.timeInSec)
    }, [props.timeInSec])

    return (
        <View>
            <Text>{ timeInSec }</Text>
        </View>
    )
}

export default Timer

Upvotes: 1

Views: 1137

Answers (1)

MuZak
MuZak

Reputation: 301

The problem had nothing to do with the useEffect... The component just didn't rerendered after the state updade because I was updating it with the same value. So obviously the useEffect wasn't triggered.

Upvotes: 1

Related Questions