0

I am building a react-native app and using react-native-tab-view in my app.

my screen has two tabs, each tab has one text field.

the state is in MyScreen, and I passed the state and setState as props to the tabs.

I added useEffect to log the state when it is changed, the state is updated correctly when I type text in any of the two fields on child tabs.

the screen is adding a save button to the header right of the navigation bar.

my problem that the state is updated but when I click save it seems to be set back to initial value as the log shows in saveState function;

please advise.

import React, { useState, useEffect, useContext } from "react";
import { StyleSheet, View, Dimensions, TextInput, ScrollView, TouchableOpacity, Picker, Switch } from "react-native";
import { TabView, TabBar } from "react-native-tab-view";
import DateTimePicker from "@react-native-community/datetimepicker";

const Tab1 = ({ navigation, state, setState }) => {
    return (
        <View style={styles.scene}>
            <ScrollView>
                <SansBoldText style={styles.label}>First</SansBoldText>
                <TextInput style={styles.field} value={state.first} onChangeText={(text) => setState((prevState) => ({ ...prevState, first: text }))} />
            </ScrollView>
        </View>
    );
};

const Tab2 = ({ navigation, state, setState }) => {
    return (
        <View style={styles.scene}>
            <ScrollView>
                <SansBoldText style={styles.label}>Second</SansBoldText>
                <TextInput style={styles.field} value={state.second} onChangeText={(text) => setState((prevState) => ({ ...prevState, second: text }))} />
            </ScrollView>
        </View>
    );
};

const initialLayout = { width: Dimensions.get("window").width };

export default function MyScreen({ navigation }) {
    const [index, setIndex] = useState(0);
    const [routes] = useState([
        { key: "tab1", title: "Tab1" },
        { key: "tab2", title: "Tab2" },
    ]);

    const [state, setState] = useState({
        first: "",
        second: "",
    });

    const saveState = () => {
        console.log(state); // this prints the initial state even after it is already changed
    };

    useEffect(() => {
        navigation.setOptions({
            headerRight: () => {
                return (
                    <TouchableOpacity activeOpacity={0.5} style={{ paddingRight: 10 }} onPress={saveState}>
                        <Ionicons name="md-save" size={30} />
                    </TouchableOpacity>
                );
            },
        });
    }, []);
    
    useEffect(() => {
        console.log("state changed = ", state);
    }, [state]);

    const renderScene = ({ route }) => {
        switch (route.key) {
            case "tab1":
                return <Tab1 navigation={navigation} state={state} setState={setState} />;
            case "tab2":
                return <Tab2 navigation={navigation} state={state} setState={setState} />;
            default:
                return null;
        }
    };

    const renderTabBar = (props) => <TabBar {...props} indicatorStyle={{ backgroundColor: "black" }} style={{ backgroundColor: "#e0e0e0" }} labelStyle={{ color: "#000" }} />;

    return <TabView navigationState={{ index, routes }} renderTabBar={renderTabBar} renderScene={renderScene} onIndexChange={setIndex} initialLayout={initialLayout} />;
}
anwar
  • 103
  • 1
  • 10

1 Answers1

1

@anwar add state as a dependency in the first useEffect also passing the second argument [state] as you did it in the second useEffect.

Abhishek
  • 281
  • 4
  • 16
  • I don't use the state as soon as it changes. in the second `useEffect` the console prints the state with the new values, then I click save. So the state should have been updated already. is not that right? – anwar Jul 31 '20 at 11:46
  • @anwar add state as a dependency in the first useEffect also passing the second argument [state] as you did it in the second useEffect. check if it updates? – Abhishek Jul 31 '20 at 11:51
  • thanks @john, adding the state as dependency to the first effect solved the issue. but can you explain why? – anwar Jul 31 '20 at 11:58
  • @anwar Yes because you are changing the dependency/state in that useEffect in the onPress but for re-rendering you have to pass the second argument inside [], it re-renders only when that state changes, if nothing is passed inside [], It will work as componentDidMount that works only on first mount, check the lifecycle methods how it is performed in hooks. – Abhishek Jul 31 '20 at 12:08
  • @anwar I have changed the answer If it helped, can you accept the answer so that someone can refer :) – Abhishek Jul 31 '20 at 12:12