Quantcast
Channel: Active questions tagged react-native+typescript - Stack Overflow
Viewing all articles
Browse latest Browse all 6214

React hooks - Updated state not re-rendering

$
0
0

Using the following useEffect hook, the updated state is not rendered unless the user touches the screen or even if we place a console.log under the effect (as indicated in the snippet below):

 export const ExerciseForm = ({  definition: { id },}: {  definition: ExerciseDefinition;}) => {  const initialSet: Set[] = [{ reps: 0, value: 5 }];  const [sets, setSets]: [Set[], Dispatch<Set[]>] = useState(initialSet);  // Reset form if definition ID changes  useEffect(() => {    setSets(initialSet);  }, [id]);  // Uncommenting this will make it the rerender work??  // console.log('id', id);

Each id is obviously unique and can confirm that it is updating correctly higher in the component tree from which the prop is passed. I realise this is a common issue, however, none of the similar answers on S/O (or elsewhere on the internets) has yielded a proper solution.

Full components can be found below:

ExerciseForm.tsx

import React, { useState, Dispatch, useEffect } from 'react';import { ExerciseDefinition, Set } from '../constants/Interfaces';import { ScrollView, View } from 'react-native';import { Input, Button } from 'react-native-elements';export const ExerciseForm = ({  definition: { id },}: {  definition: ExerciseDefinition;}) => {  const initialSet: Set[] = [{ reps: 0, value: 5 }];  const [sets, setSets]: [Set[], Dispatch<Set[]>] = useState(initialSet);  // Reset form if definition ID changes  useEffect(() => {    setSets(initialSet);  }, [id]);  // Uncommenting this will make it the rerender work??  // console.log('id', id);  const updateSet = (index: number, field: 'reps' | 'value', value: string) => {    const updatedSets = [...sets];    updatedSets[index][field] = parseInt(value);    setSets(updatedSets);  };  const addSet = () => {    const updatedSets = [...sets, { ...sets[sets.length - 1] }];    setSets(updatedSets);  };  return (<ScrollView>      {sets.map(({ reps, value }: Set, index: number) => (<View key={index}><Input            label="Reps"            keyboardType="numeric"            value={reps ? reps.toString() : ''}            onChange={({ nativeEvent }) =>              updateSet(index, 'reps', nativeEvent.text)            }          /><Input            label="Weight"            keyboardType="numeric"            value={value ? value.toString() : ''}            onChange={({ nativeEvent }) =>              updateSet(index, 'value', nativeEvent.text)            }          /></View>      ))}<Button title="Add set" onPress={() => addSet()} /></ScrollView>  );};

HomeScreen.tsx

import React, {  useContext,  useReducer,  useEffect,  useState,  Dispatch,} from 'react';import {  StyleSheet,  Picker,  ScrollView,  ActivityIndicator,} from 'react-native';import { Text } from '../components/Themed';import { UserContext } from '../services/context';import {  exerciseDefinitionReducer,  initialExerciseDefinitionState,  exerciseDefinitionActions,} from '../reducers/exerciseDefinition';import { ExerciseDefinition } from '../constants/Interfaces';import { ExerciseForm } from '../components/ExerciseForm';export default function HomeScreen() {  const {    state: { firstName },  } = useContext(UserContext);  const [{ definitions, loading }, definitionDispatch] = useReducer(    exerciseDefinitionReducer,    initialExerciseDefinitionState  );  const [selectedDefintion, setSelectedDefinition]: [    ExerciseDefinition | undefined,    Dispatch<ExerciseDefinition>  ] = useState();  // Get definitions on mount  useEffect(() => {    exerciseDefinitionActions(definitionDispatch).getDefinitions();  }, []);  // Set default definition to first item  useEffect(() => {    !selectedDefintion && setSelectedDefinition(definitions[0]);  }, [definitions]);  return (<ScrollView><Text style={styles.title}>{firstName}</Text>      {loading && <ActivityIndicator />}      {/* TODO: decide whether to use this R/N Picker or NativeBase picker */}<Picker        selectedValue={selectedDefintion?.id}        onValueChange={(id) => {          const definition = definitions.find((def) => def.id === id);          definition && setSelectedDefinition(definition);        }}>        {definitions.map((defintion) => {          const { title, id } = defintion;          return <Picker.Item key={id} label={title} value={id} />;        })}</Picker>      {selectedDefintion && <ExerciseForm definition={selectedDefintion} />}</ScrollView>  );}const styles = StyleSheet.create({  container: {    flex: 1,    alignItems: 'center',    justifyContent: 'center',  },  title: {    fontSize: 20,    fontWeight: 'bold',  },  separator: {    marginVertical: 30,    height: 1,    width: '80%',  },});

(Edit 29/09/2020)

Interestingly, neither of the following console.logs are executed either until the user touches the screen (or the noted commented lined is uncommented), suggesting that the issue is certainly to do with the useEffect statement or its dependency:

  // Reset form if definition ID changes  useEffect(() => {    console.log('old ID', id);    setSets(initialSet);    console.log('new ID', id);  }, [id]);  // Uncommenting this will make it the rerender work??  // console.log('id', id);

Viewing all articles
Browse latest Browse all 6214

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>