React Native待办项点击切换样式问题求助及代码排查
解决React Native待办项点击样式修改的问题
我来帮你搞定这个问题,你的代码里有几个小细节没处理好,咱们逐个解决:
1. 函数触发时机错误(渲染时就执行了markTodo)
你在TouchableOpacity的onPress里写的是this.markTodo(todo),这相当于在组件渲染的时候直接调用了函数,而不是把函数绑定到点击事件上。正确的做法是用箭头函数包裹,确保只有点击时才执行:
<TouchableOpacity onPress={() => this.markTodo(todo)} style={{ width: 20, height: 20, borderRadius: 15, borderWidth: 3, borderColor: 'white', margin: 25, }} />
2. 修改State时违反了不可变原则
React要求我们不能直接修改原state对象/数组,否则React无法检测到state变化,组件可能不会重新渲染。你的markTodo里直接操作了原todos数组,改成下面这样:
markTodo = (selectedTodo) => { // 先复制原数组,创建一个新数组 const updatedTodos = [...this.state.todos]; // 通过唯一标识(createdAt)找到对应的待办项索引 const todoIndex = updatedTodos.findIndex(todo => todo.createdAt === selectedTodo.createdAt); if (todoIndex !== -1) { // 切换选中状态(如果需要点击切换,而不是只设为选中) updatedTodos[todoIndex].isSelected = !updatedTodos[todoIndex].isSelected; this.setState({ todos: updatedTodos }); } }
这里用createdAt作为唯一标识比indexOf更可靠,因为数组反转后索引会变化,indexOf可能找不到正确的项。
3. 样式应用位置错误
你把颜色样式加在了View组件上,但View的color属性不会影响内部的Text文字颜色。需要把条件样式移到Text组件的style里:
<Text style={{ paddingLeft: 5, marginTop: 10, fontSize: 28, color: todo.isSelected ? "red" : "white" }}> {todo.title} </Text>
如果想给整个待办项添加其他视觉变化(比如选中时加边框),可以给外层View加对应的条件样式,比如:
<View style={[ { flexDirection: 'row', marginTop: 20 }, todo.isSelected ? { borderLeftWidth: 4, borderLeftColor: 'red' } : {} ]}>
完整修正后的代码
import * as React from 'react'; import { StatusBar, View, TextInput, TouchableOpacity, Text, } from 'react-native'; import { LinearGradient } from 'expo-linear-gradient'; export default class App extends React.Component { state = { inputValue: '', todos: [], }; changeText = value => { this.setState({ inputValue: value, }); }; addItem = () => { if (this.state.inputValue !== '') { this.setState(prevState => { const newToDo = { title: this.state.inputValue, isSelected: false, createdAt: Date.now(), }; // 同样遵循不可变原则,用concat创建新数组 const todos = prevState.todos.concat(newToDo); return { inputValue: '', todos: todos, }; }); } }; markTodo = (selectedTodo) => { const updatedTodos = [...this.state.todos]; const todoIndex = updatedTodos.findIndex(todo => todo.createdAt === selectedTodo.createdAt); if (todoIndex !== -1) { updatedTodos[todoIndex].isSelected = !updatedTodos[todoIndex].isSelected; this.setState({ todos: updatedTodos }); } } render() { const todos = this.state.todos.reverse().map((todo, key) => ( <View key={todo.createdAt} style={[ { flexDirection: 'row', marginTop: 20 }, todo.isSelected ? { borderLeftWidth: 4, borderLeftColor: 'red' } : {} ]}> <TouchableOpacity onPress={() => this.markTodo(todo)} style={{ width: 20, height: 20, borderRadius: 15, borderWidth: 3, borderColor: 'white', margin: 25, }} /> <Text style={{ paddingLeft: 5, marginTop: 10, fontSize: 28, color: todo.isSelected ? "red" : "white" }}> {todo.title} </Text> </View> )); return ( <LinearGradient colors={['#667eea', '#764ba2']} style={{ flex: 1 }}> <StatusBar barStyle="light-content" /> <View> <TextInput style={styles.input} onSubmitEditing={this.addItem} onChangeText={this.changeText} placeholder="Type here to add a to do." value={this.state.inputValue} placeholderTextColor={'#fff'} multiline={true} autoCapitalize="sentences" underlineColorAndroid="transparent" selectionColor={'white'} maxLength={30} returnKeyType="done" autoCorrect={false} blurOnSubmit={true} /> </View> <View>{todos}</View> </LinearGradient> ); } } const styles = { input: { marginTop: 30, paddingTop: 10, paddingRight: 15, paddingLeft: 15, fontSize: 34, color: 'white', fontWeight: '500', }, };
另外我还给每个待办项的View加了key={todo.createdAt},这是React列表渲染的最佳实践,避免渲染警告。
内容的提问来源于stack exchange,提问作者Akshay Bhat
相关产品推荐
相关产品推荐

