Dan Cruz
Dan Cruz

Reputation: 92

Screen appears to unexpectedly render twice after mounting. React Native

I'm trying to solve an issue I'm having with RN. I'm working with the Philips Hue API. I have one screen that is part of a tab navigation and I have a stack navigation nested inside one of the tabs. I have a screen that shows all lights connected to the hub and when you select one lightbulb it navigates you to a detail screen.

Below is my "All Lights" screen:

<FlatList
data={allLights}
renderItem={({ item, index }) => {
console.log(item, "from light icon");
return (
<TouchableOpacity
onPress={() =>
navigation.navigate("LightDetail", {
lightbulb: item,
lightId: index + 1,
                })
}
>
<LightBulb
lightName={item.name}
isOn={item.state.on}
lightId={index + 1}
/>
</TouchableOpacity>
          );
        }}
/>

You will notice I'm passing in data as 'lightbulb'.

Below is my "Light Detail" screen:

    export const LightDetailScreen = ({ route }) => {
    const { lightbulb, lightId } = route.params;
    console.log(lightbulb.state.sat, lightbulb.state.bri, "from params");
    const [lightBulbState, setLightBulbState] = useState({
    sat: lightbulb.state.sat,
    hue: lightbulb.state.hue,
    bri: lightbulb.state.bri,
    isOn: lightbulb.state.on,
      });
    console.log(lightBulbState);
    const controlLight = async (lightID, on, hue, sat, bri) => {
    console.log(hue, sat, bri, "from control light");
    try {
    return await huePhil.put(\/lights/${lightID}/state`, {on,sat,bri,hue,     });   } catch (err) {console.log(err);   } };const toggleLight = async () => {setLightBulbState((prev) => {return { ...prev, isOn: !prev.isOn };   });controlLight(lightId,lightBulbState.isOn,lightBulbState.hue,lightBulbState.sat,lightBulbState.bri   ); };const upDateBri = (value) => {setLightBulbState((prev) => {return { ...prev, bri: Math.floor(value) };   });controlLight(lightId,lightBulbState.isOn,lightBulbState.hue,lightBulbState.sat,lightBulbState.bri   ); };const upDateSat = (value) => {setLightBulbState((prev) => {return { ...prev, sat: Math.floor(value) };   });controlLight(lightId,lightBulbState.isOn,lightBulbState.hue,lightBulbState.sat,lightBulbState.bri   ); };return (<SafeArea><View style={styles.container}><Text>{lightbulb.name} </Text><View style={styles.sliderContainer}><Sliderstyle={{ width: 300, height: 40 }}minimumValue={0}maximumValue={254}minimumTrackTintColor="#FFFFFF"maximumTrackTintColor="#000000"value={lightbulb.state.sat}onValueChange={upDateSat}/><Text>Saturation: {lightBulbState.sat || lightbulb.state.sat}</Text></View><View style={styles.sliderContainer}><Sliderstyle={{ width: 300, height: 40 }}minimumValue={0}maximumValue={254}minimumTrackTintColor="#FFFFFF"maximumTrackTintColor="#000000"value={lightbulb.state.bri}onValueChange={upDateBri}/><Text>Brightness: {lightBulbState.bri || lightbulb.state.bri}</Text></View><View style={styles.btnContainer}><Buttontitle={lightBulbState.isOn ? "currently on" : "currently off"}onPress={toggleLight}/></View></View></SafeArea> );`

The three logs that I have bold faced are logging 3 times each. Here is the data from the logs:

93 71 from params

Object {

"bri": 71,

"hue": 8417,

"isOn": true,

"sat": 93,

}

8417 93 71 from control light

93 71 from params

Object {

"bri": 71,

"hue": 8417,

"isOn": true,

"sat": 0,

}

8417 0 71 from control light

93 71 from params

Object {

"bri": 0,

"hue": 8417,

"isOn": true,

"sat": 0,

}

I would expect to only see the logs once instead I get them 3 times. Also even if it did log more than once I would expect the data to remain the same every time but as you can see on the second iteration I'm losing "sat" and on the third I'm losing "bri"

What could be causing this behavior? I hope someone can point me in the right direction.

Thanks!

Upvotes: 0

Views: 915

Answers (1)

Julian K
Julian K

Reputation: 2001

It's pretty normal to have re-renders. But if you want to avoid them, such as for performance reasons (which tends to be a good idea), you need to identify why it's re-rendering.

For this, you can use the tool Why Did You Render. It will compare props and see which props have changed. In this case, it's probably lightbulb, because as it's an object, during each render tick lightbulb (old) !== lightbulb (new). This is because object instances are not equal to each other ({} !== {}). You can get around this by passing lightbulb props as individual props (i.e. LightDetailScreen = ({hue, sat, bri, on}) => etc...). You may also need to wrap your LightDetailScreen in React.memo, so that the same component instance is returned when the props are the same.

That said, the (probably) only reason it's re-rendering in the first place is because a higher up component is re-rendering as well, so you could also investigate your root component and see why it's mounting 3 times.

If you'd like more ideas, post more code and maybe we can pinpoint where the extra renders are coming from.

Upvotes: 1

Related Questions