如何让React的setState立即更新值?解决按钮需点击两次问题
解决点击两次按钮才更新电影筛选的问题
问题根源
你遇到的问题确实是setState的异步特性导致的:当你在onPress中调用setFilter后,React不会立刻更新filter的状态值,所以紧接着调用getMovies(filter)时,传入的还是更新前的旧filter值。第一次点击时,实际是先把filter设为新值,但请求用的还是旧值;第二次点击时,filter已经是上一次设置的新值,请求才会生效,所以需要点击两次。
另外你的代码里还有两个小问题:
- 定义了监听
filter变化的useEffect,但里面调用的getMoviesFiltered函数不存在,应该是getMovies movies的初始值设为{},但FlatList的data需要数组类型,应该初始化为[]
修正方案(推荐第一种)
方案1:利用useEffect自动触发请求
完全依赖useEffect监听filter的变化来调用getMovies,去掉onPress里的getMovies调用,这样状态更新后会自动触发数据请求:
const HomeScreen = () => { const navigation = useNavigation(); // 初始值改为数组,适配FlatList的data要求 const [movies, setMovies] = useState([]); const [filter, setFilter] = useState('name'); const getMovies = async (currentFilter) => { const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(currentFilter))); setMovies(querySnapshot.docs); } // 初始化请求,传入初始filter值 useEffect(() => { getMovies(filter); },[]) // 监听filter变化,自动触发对应筛选请求 useEffect(() => { getMovies(filter); },[filter]) return ( <View> <View> {/* 只需要设置filter,请求逻辑交给useEffect处理 */} <TouchableOpacity onPress={() => setFilter('name')}> <Text>Title</Text> </TouchableOpacity> <TouchableOpacity onPress={() => setFilter('runtime')}> <Text>Runtime</Text> </TouchableOpacity> <TouchableOpacity onPress={() => setFilter('year')}> <Text>Year</Text> </TouchableOpacity> </View> <FlatList data={movies} renderItem={({item}) => ( <TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}> <Image source={{uri: item.data().pic}}/> </TouchableOpacity> )} /> </View> ) } export default HomeScreen
方案2:直接传递目标filter值给getMovies
如果不想用useEffect,可以在onPress里直接把要设置的filter值传给getMovies,绕过状态异步的问题:
const HomeScreen = () => { const navigation = useNavigation(); const [movies, setMovies] = useState([]); const [filter, setFilter] = useState('name'); const getMovies = async (currentFilter) => { const querySnapshot = await getDocs(query(collection(db, "movies"), orderBy(currentFilter))); setMovies(querySnapshot.docs); } useEffect(() => { getMovies(filter); },[]) return ( <View> <View> <TouchableOpacity onPress={() => { const newFilter = 'name'; setFilter(newFilter); getMovies(newFilter); // 直接传递新值,不依赖当前状态 }}> <Text>Title</Text> </TouchableOpacity> <TouchableOpacity onPress={() => { const newFilter = 'runtime'; setFilter(newFilter); getMovies(newFilter); }}> <Text>Runtime</Text> </TouchableOpacity> <TouchableOpacity onPress={() => { const newFilter = 'year'; setFilter(newFilter); getMovies(newFilter); }}> <Text>Year</Text> </TouchableOpacity> </View> <FlatList data={movies} renderItem={({item}) => ( <TouchableOpacity onPress={() => navigation.navigate('Modal', item.data())}> <Image source={{uri: item.data().pic}}/> </TouchableOpacity> )} /> </View> ) } export default HomeScreen
说明
第一种方案更符合React的状态管理逻辑,让状态变化和副作用(数据请求)分离,代码更简洁易维护;第二种方案适合简单场景,直接避免异步状态的问题。
内容的提问来源于stack exchange,提问作者Mihailo
相关产品推荐
相关产品推荐

