简体   繁体   中英

React native - State doesnt properly update

Hello together I'm actually building a note app where you can add some notes to a specific date in calendar.

I'm using the wix-agenda component for the calendar. When clicking on the the grey checkbox I update the state of the item to be checked, but the state doesnt get the update. When I save my file in editor again (using Hotload) the component gets updated and the checkbox appears as yellow. Anyone a hint?

const SCREEN_WIDTH = Dimensions.get('window').width;
const SCREEN_HEIGHT = Dimensions.get('window').height;

const customAnimation = {
  duration: 100,
  create: {
    type: LayoutAnimation.Types.linear,
    property: LayoutAnimation.Properties.opacity,
  },
  update: {
    type: LayoutAnimation.Types.linear,
  }
};

class CalendarScreen extends Component {
    constructor(props) {
        super(props);

        this.state = {
          items: {
            '2018-08-20': [
            {key: '2018-08-20', name: 'Test', checked: true },
            {key: '2018-08-20', name: 'Test', checked: false },
            {key: '2018-08-20', name: 'Test', checked: false },
          ],
          '2018-08-21': [

          ],
          '2018-08-22': [
            {key: '2018-08-22', name: 'Test!', checked: true },
            {key: '2018-08-22', name: 'Test', checked: false },
            {key: '2018-08-22', name: 'Test', checked: true },
          ],
        },
          reason: '',
          itemsOld: {},
          isModalVisible: false,
          noteDate: new Date(),
          noteText: '',
          isLoading: false
        };
      }
      /* async componentWillMount() {
        let notes = await AsyncStorage.getItem('notes');
        if (!_.isNull(notes)) {
          console.log('>>>>>>>>'+ notes)
          this.setState({ items: notes, isLoading: false });
          console.log("###### MOUNT #######");
          console.log("ITEMS: >>> "+ this.state);
        }
      }*/

      componentWillUpdate() {
        LayoutAnimation.linear();
        LayoutAnimation.configureNext(customAnimation);
      }

      /*async componentWillUnmount() {
        await AsyncStorage.setItem('notes', JSON.stringify(this.state.items));
      }*/

      onToastClose(reason) {
        this.setState({ reason });
        if (reason === 'user') {
          this.setState({ items: this.state.itemsOld });
        }
      }


      setDate(newDate) {
        newDate.setDate(newDate.getDate()+1);
        date = newDate.toISOString().split('T')[0];
        this.setState({ noteDate: date });
      }

      loadItems(day) {

        // console.log(`Load Items for ${day.year}-${day.month}`);
      }

  addNote() {
    oldState = { ...this.state.items };
    arrayToPush = oldState[this.state.noteDate];
    if (_.isNil(arrayToPush)) {
        arrayToPush = [];
    }
      arrayToPush.push({key: this.state.noteDate, name: this.state.noteText, checked: false });
    oldState[this.state.noteDate] = arrayToPush;
    this.setState({ items: oldState, noteText: '', isModalVisible: false });
  }

  removeItem(item) {
    //Copy state
    oldState = { ...this.state.items };
    //Get actual clicked key -> date of note is the key
    actualArray = [...this.state.items[item.key]];

    // Get the index in the Array with the notes
    index = actualArray.indexOf(item);

    //remove the object from the array!!
    actualArray.splice(index,1);

    //Copy previous state
    newState = { ...this.state.items };

    //Update array with key of clicked element
    newState[item.key] = actualArray;

    Toast.show({
      text: 'Sie haben eine Notiz entfernt!',
      buttonText: 'Rückgangig',
      duration: 3000,
      onClose: this.onToastClose.bind(this)
    });
    //Update state --- MAGIC!
    this.setState({ items:  newState, itemsOld: oldState });
  }

  toggleCheck(item) {
    //Get actual clicked key -> date of note is the key
    actualArray = [...this.state.items[item.key]];
    // Get the index in the Array with the notes
    index = actualArray.indexOf(item);

    //change the checkmark!!
    actualArray[index].checked = !actualArray[index].checked;

    //Copy previous state
    newState = { ...this.state.items };

    //Update array with key of clicked element
    newState[item.key] = actualArray;

    //Update state --- MAGIC!
    this.setState({ items:  newState });
  }

  _toggleModal = () => {

    this.setState({ isModalVisible: !this.state.isModalVisible });
  }


  rowHasChanged(r1, r2) {
    return r1.name !== r2.name;
  }

  timeToString(time) {
    const date = new Date(time);
    return date.toISOString().split('T')[0];
  }
  renderDay(day,item) {
    if (day) {
    return (
    <View style={styles.day}>
      <Text allowFontScaling={false} style={[styles.dayNum]}>{day.day }</Text>
      <Text allowFontScaling={false} style={[styles.dayText]}>{day.text}</Text>
    </View>
    );
      }
      return (
          <View style={styles.day}/>
        );
      }

  renderEmptyDate() {a
    return (
      <View style={styles.emptyDate}><Container><Text>This is empty date!</Text></Container></View>
    );
  }

  renderItem(item) {
    color = item.checked ? "#F7D23D" : '#ccc';
    return (
        <Container
        style={[styles.item, { height: 70, flexDirection: 'row', alignItems: 'center' }]}
        >
          <Body style={{ flex: 5 }}>
            <Text>{item.name}</Text>
          </Body>
          <Body style={{ flex: 1 }}>
            <MaterialIcons
            size={24} name="check-circle" color={color}
            style={{ alignSelf: 'center', }}
            onPress={() => {
              this.toggleCheck(item);              
            }}
            />
          </Body>
        </Container>
    );
  }

  render() {
    if (this.state.isLoading) {
      return <View><Text>Loading...</Text></View>;
    }
    return (
      <Container>
            <Header textStyle={{ color: 'white' }}>
            <Left >
            <Button light transparent style={styles.whiteIcon} onPress={() => this.props.navigation.dispatch(DrawerActions.openDrawer())}>
              <Icon name='ios-menu' />
            </Button>
            </Left>
            <Body>
                <Title style={styles.headerTextStyle}> KALENDER </Title>
            </Body>
            <Right>
                <Button light transparent>
                <Icon name='search' />
                </Button>
            </Right>
            </Header>
            <Container>
      <Agenda
        //https://github.com/wix/react-native-calendars
        items={this.state.items}
        loadItemsForMonth={this.loadItems.bind(this)}
        selected={'2018-08-20'}
        renderItem={this.renderItem.bind(this)}
        renderEmptyDate={() => {return (<View />);}}
        renderEmptyData = {() => {return (<View />);}}
        rowHasChanged={this.rowHasChanged.bind(this)}
         theme={{ selectedDayBackgroundColor: '#F7D23D', todayTextColor: '#F7D23D', dotColor: '#F7D23D',}}
         renderDay={this.renderDay.bind(this)}
      />
          <Modal
          avoidKeyboard
          isVisible={this.state.isModalVisible}
          animationType="fade"
          onBackdropPress={() => this._toggleModal()}
          onBackButtonPress={() => this._toggleModal()}
          style={{ justifyContent: 'flex-end'}}
          backdropOpacity={0.5}
          >
          <Card>
            <CardItem header>
              <Text>Notiz erstellen</Text>
            </CardItem>
            <CardItem>
            <Form style={{ flex: 1}}>
            <DatePicker
            defaultDate={new Date(2018, 8, 20)}
            minimumDate={new Date(2018, 1, 1)}
            maximumDate={new Date(2018, 12, 31)}
            locale={"de"}
            modalTransparent={false}
            animationType={"fade"}
            androidMode={"default"}
            placeHolderText="Datum wählen"
            placeHolderTextStyle={{ color: "#d3d3d3" }}
            onDateChange={this.setDate.bind(this)}
            />
            <Textarea rowSpan={3} placeholder="Notiz" value={this.state.noteText} onChangeText={text => this.setState({ noteText: text })} />
          </Form>
            </CardItem>
            <CardItem footer>
            <Left>
              </Left>
              <Body>
              <Button transparent onPress={()=>{this._toggleModal()}}>
                  <Text>Abbrechen</Text>
                </Button>
              </Body>
              <Right>
              <Button transparent onPress={this.addNote.bind(this)}>
                <Text>Hinzufügen</Text>
                </Button>
              </Right>
            </CardItem>
          </Card>
          </Modal>
      <Fab
            style={{ backgroundColor: '#F7D23D' }}
            position="bottomRight"
            onPress={() => {
              this.setState({ active: !this.state.active });

              this._toggleModal();
              }}
            >
            <MaterialIcons size={24} name="add" />
          </Fab>
      </Container>  
      </Container>
    );
  }

  //COMPONENT END
}


const styles = StyleSheet.create({
  item: {
    backgroundColor: 'white',
    flex: 1,
    borderRadius: 5,
    padding: 10,
    marginRight: 10,
    marginTop: 10

  },
  emptyDate: {
    height: 15,
    flex:1,
    paddingTop: 30
  },
  dayNum: {
    fontSize: 28,
    fontWeight: '200',
    color: 'black'
  },
  dayText: {
    fontSize: 14,
    fontWeight: '300',
    color: 'black',
    marginTop: -5,
    backgroundColor: 'rgba(0,0,0,0)'
  },
  day: {
    width: 63,
    alignItems: 'center',
    justifyContent: 'flex-start',
    marginTop: 32
  },
});

export default CalendarScreen;

the main part is about here

 toggleCheck(item) {
    //Get actual clicked key -> date of note is the key
    actualArray = [...this.state.items[item.key]];
    // Get the index in the Array with the notes
    index = actualArray.indexOf(item);

    //change the checkmark!!
    actualArray[index].checked = !actualArray[index].checked;

    //Copy previous state
    newState = { ...this.state.items };

    //Update array with key of clicked element
    newState[item.key] = actualArray;

    //Update state --- MAGIC!
    this.setState({ items:  newState });
  }

Any advise?

The keywords let and const appear to be missing for the local variables declared inside of the toggleCheck method, resulting in assignments to global variables instead of ones that are local to the callbacks—in particular, the variables actualArray , index , and newState are shared across all invocations of those callbacks.

This is also the case in the methods setDate , addNote , removeItem and renderItem .

Here's how you might fix toggleCheck :

toggleCheck(item) {
  const actualArray = [...this.state.items[item.key]];
  const index = actualArray.indexOf(item);
  actualArray[index].checked = !actualArray[index].checked;

  const newState = { ...this.state.items };
  newState[item.key] = actualArray;

  this.setState({ items: newState });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM