使用以下useEffect
钩子,除非用户触摸屏幕,或者即使我们将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
显然是唯一的,并且可以确认它在传递道具的组件树中正确地更新了。我意识到这是一个普遍的问题,但是,关于S / O(或Internet上其他地方)的类似答案均未找到合适的解决方案。
完整的组件可以在下面找到:
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%',
},
});
(编辑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);