Zakher Masri
Zakher Masri

Reputation: 1176

AsyncStorage.getItem in componentDidMount

I'm trying to create a simple to-do application to practice React-Native and I'm having trouble retrieving the items when the componentDidMount

The app state :

this.state = {
  task: "",
  tasks: []
};

When the user adds a new task handleAddTask runs and I attached the AsyncStorage.setItem as a callback for setState so that the tasks array is saved to the storage after the state is updated.

handleAddTask = () => {

  let notEmpty = this.state.task.trim().length > 0;
  if (notEmpty) {
    this.setState(
      prevState => {
        return {
          tasks: prevState.tasks.concat(prevState.task)
        };
      },() => {
        AsyncStorage.setItem("tasks", JSON.stringify(this.state.tasks));
      }
    );
  }

};

Similarly, when the user taps on a list item handleDelete runs, it removes the selected task from the tasks array and updates the AsyncStorage

handleDelete = index => {

  this.setState(
    prevState => {
      return {
        tasks: prevState.tasks.filter((place, i) => {
          return i !== index;
        })
      };
    },() => {
      AsyncStorage.setItem("tasks", JSON.stringify(this.state.tasks));
    }
  );

};

However, I'm trying to retreive the tasks array from AsyncStorage inside componentDidMount like so :

componentDidMount() {
  AsyncStorage.getItem("tasks")
    .then(value => {
      this.setState({ "tasks": value });
    })
    .done();
}

The way I'm displaying the tasks list is by passing the state through a component called List:

<List
  listItems={this.state.tasks}
  handleDelete={this.handleDelete}
/>

Inside List.js I'm mapping through the tasks array:

export default class List extends Component {

  render() {

    const namesOutput = this.props.listItems.map((name, i) => (
      <TouchableOpacity
        key={i}
        onPress={() => this.props.handleDelete(i)}
        activeOpacity={0.6}
        style={styles.listItemContainer}
      >
        <Text style={styles.listItemText}>{name}</Text>
      </TouchableOpacity>
    ));

    return (
      <View style={styles.listContainer}>
        <ScrollView>{namesOutput}</ScrollView>
      </View>
    );
  }

}

And I get the following error:

TypeError: undefined is not a function (evaluating 'this.props.listItem.map')

I've been commenting lines here and there to see where the problems is and I can only assume that it's in the way I'm getting the items in componentDidMount

Is there something about asynchronous JavaScript that I missed? Also, do I have to JSON.parse when I do getItem ? If anyone has additional feedback about my code in general I'd love to learn more.

Thanks in advance!

Upvotes: 0

Views: 4180

Answers (2)

Isaac Sekamatte
Isaac Sekamatte

Reputation: 5598

AsyncStorage returns a promise. Therefore when you call to retrieve items in componentDidMount, the code will run passed the .then even before the values are retrieved.

My suggestion is that you give listItem some dummy objects so that it is not undefined the first it is called.

Second suggestion would be use await instead of .then and use a helper function when calling listenItem.map so that it's not called until values have been loaded.

Upvotes: 0

Yossi
Yossi

Reputation: 445

You saved to storage JSON.stringify() values, then used it without using JSON.parse(). I think that this is the problem. You can check it by stop on break point on your List component render before it maps its props, and detect that your props.listItems isn't array after componentDidMount.

Upvotes: 1

Related Questions