I’m building an app with React Native and Firebase Realtime Database, when I start the function addItem the only parameter in the object that I get from the Database is the id, the other paramters return as '', I’ve tried using console.log() and the TextInputs work fine also I’ve used this configuration a lot of times and this is the first time it happens.
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [])
const addItem = () => {
const changes = ref(db, 'path')
get(changes).then( async (snapshot) => {
if (snapshot.val().data !== undefined) {
let array = []
let object = {
"id": `${id}`,
"name": name,
"price": price,
"description": description,
}
array.push(object)
update(changes, {
data: array
})
}
})
}
return (
<TouchableWithoutFeedback>
<TextInput
onChangeText={(e) => setName(e)}
/>
<TextInput
onChangeText={(e) => setShippingPrice(e)}
/>
<TextInput
onChangeText={(e) => setPrice(e)}
/>
<TextInput
onChangeText={(e) => setDescription(e)}
/>
</TouchableWithoutFeedback>
)
}
>Solution :
addItem is a stale enclosure over the state it references since the useEffect hook runs only once and closes over the initial state. You can likely resolve by re-enclosing the updated state by adding addItem function to the dependency array.
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, [addItem]);
You should memoize the addItem callback handler to further reduce the number of times the useEffect hook above re-encloses the callback/state.
const addItem = React.useCallback(() => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}, [name, price, description]);
Alternatively you could cache all the state values in a React ref and access them via the ref in the callback.
Example:
export default function NewItem() {
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [description, setDescription] = useState('');
const stateRef = React.useRef({
name,
price,
description,
});
useEffect(() => {
stateRef.current = { name, price, description };
}, [name, price, description]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity onPress={addItem}>
<Text style={{fontWeight:'bold'}}>ADD ITEM</Text>
</TouchableOpacity>
)
})
}, []);
const addItem = () => {
const changes = ref(db, 'path');
get(changes)
.then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const { name, price, description } = stateRef.current;
const data = [{
"id": `${id}`,
name,
price,
description,
}];
update(changes, { data });
}
})
}
return (...);
}