You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:06:35