#reactjs #typescript #react-native #react-hooks
#reactjs #typescript #реагировать — родной #реагирующие крючки
Вопрос:
Используя следующий useEffect
перехват, обновленное состояние не отображается, если пользователь не коснется экрана или даже если мы поместим a console.log
под эффектом (как указано в приведенном ниже фрагменте):
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);
id
Очевидно, что каждый из них уникален и может подтвердить, что он правильно обновляется выше в дереве компонентов, из которого передается prop. Я понимаю, что это распространенная проблема, однако ни один из подобных ответов на S / O (или где-либо еще в Интернете) не дал правильного решения.
Полные компоненты можно найти ниже:
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 amp;amp; setSelectedDefinition(definitions[0]);
}, [definitions]);
return (
<ScrollView>
<Text style={styles.title}>{firstName}</Text>
{loading amp;amp; <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 amp;amp; setSelectedDefinition(definition);
}}
>
{definitions.map((defintion) => {
const { title, id } = defintion;
return <Picker.Item key={id} label={title} value={id} />;
})}
</Picker>
{selectedDefintion amp;amp; <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%',
},
});
(Редактирование 29/09/2020)
Интересно, что ни одно из следующих console.logs
действий не выполняется до тех пор, пока пользователь не коснется экрана (или отмеченная строка с комментариями не раскомментирована), предполагая, что проблема, безусловно, связана с useEffect
оператором или его зависимостью:
// 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);