John Carraher
John Carraher

Reputation: 63

React Native - How to execute a function after ASYNC function finishes

So I'm working on this React native image storage app. The idea of the app is to be able to store tons of images into categorized boards. Right now I'm working on the board part and I want the app to store the boards. Problem right now is that I have this function which stores rows of the boards NOT the individual boards but a row of them. This function is called "syncBoardstoBoardRow".

The problem I'm having is that whenever I call this function it's after my AsyncStorage Asynchronus "getItems" function, which gets persisted JSON files of each boards, therefore my syncBoardstoBoardRow function has no boards to work with because the function before it still hasn't finished getting all the boards.

Does anyone know how make a function wait to execute until the previous async function finishes its work? This would preferably be within my componentDidMount function.

Important Code:

state = {
    homePageDisplay: 'block',
    board1PageDisplay: 'none',
    addBoardPageDisplay: 'none',

    newBoardName: '',
    newBoardImage: '',

    currentBoardName: '',

    boards: [],
    images: [],

    boardRows:[],
  };

  addBoard = () => {
    this.handleBackPress();
    this.state.boards.splice(this.state.boards.length, 0, {
      id: this.state.boards.length + 1,
      board: this.state.newBoardName,
      imageUrl: this.state.newBoardImage,
    });
    this.setState({
      newBoardImage: '',
      newBoardName: '',
    });
    AsyncStorage.setItem('boards', JSON.stringify(this.state.boards));
    this.syncBoardstoBoardRow();
  };

  syncBoardstoBoardRow = () => {
    for(var i=0; i < this.state.boards.length; i+2){
        if (this.state.boards.length/2 != 0) {
              //If boards are odd, and this is the last row && i+1==this.state.boards.length
            this.state.boardRows.splice(this.state.boardRows.length, 0, {
              id1: this.state.boards.id[i],
              board1: this.state.boards.board[i],
              imageUrl1: this.state.boards.imageUrl[i],      
              id2: -1,
              board2: '',
              imageUrl2: 'https://wallpaperaccess.com/full/1556608.jpg',
            });
        } else {
            this.state.boardRows.splice(this.state.boardRows.length, 0, {
              id1: this.state.boards.id[i],
              board1: this.state.boards.board[i],
              imageUrl1: this.state.boards.imageUrl[i],      
              id2: this.state.boards.id[i+1],
              board2: this.state.boards.board[i+1],
              imageUrl2: this.state.boards.imageUrl[i+1],
            });
        }
    }
  };

    async getData() {
      //AsyncStorage.removeItem('images'); // Use to remove
      let result = AsyncStorage.getItem('boards', (err, result) => {
        if (result !== null) {
          this.setState({
            boards: JSON.parse(result),
          })
        }
      });
      AsyncStorage.getItem('images', (err, result) => {
        if (result !== null) {
          this.setState({ 
            images: JSON.parse(result), 
          }) 
        }
      });
    }

    componentDidMount() {
       this.getData();
       this.syncBoardstoBoardRow();
    }

All Code:

import React, { Component } from 'react';
import {
  AppRegistry,
  Text,
  View,
  StyleSheet,
  Image,
  TextInput,
  ImageBackground,
  TouchableHighlight,
  Alert,
  Dimensions,
  ScrollView,
  AsyncStorage,
} from 'react-native';
import Constants from 'expo-constants';


let deviceHeight = Dimensions.get('window').height;
let deviceWidth = Dimensions.get('window').width;

export default class App extends Component {
  state = {
    homePageDisplay: 'block',
    board1PageDisplay: 'none',
    addBoardPageDisplay: 'none',

    newBoardName: '',
    newBoardImage: '',

    currentBoardName: '',

    boards: [],
    images: [],
    
    boardRows:[],
  };

  addBoard = () => {
    this.handleBackPress();
    this.state.boards.splice(this.state.boards.length, 0, {
      id: this.state.boards.length + 1,
      board: this.state.newBoardName,
      imageUrl: this.state.newBoardImage,
    });
    this.setState({
      newBoardImage: '',
      newBoardName: '',
    });
    AsyncStorage.setItem('boards', JSON.stringify(this.state.boards));
    this.syncBoardstoBoardRow();
  };

  syncBoardstoBoardRow = () => {
    for(var i=0; i < this.state.boards.length; i+2){
        if (this.state.boards.length/2 != 0) {
              //If boards are odd, and this is the last row && i+1==this.state.boards.length
            this.state.boardRows.splice(this.state.boardRows.length, 0, {
              id1: this.state.boards.id[i],
              board1: this.state.boards.board[i],
              imageUrl1: this.state.boards.imageUrl[i],      
              id2: -1,
              board2: '',
              imageUrl2: 'https://wallpaperaccess.com/full/1556608.jpg',
            });
        } else {
            this.state.boardRows.splice(this.state.boardRows.length, 0, {
              id1: this.state.boards.id[i],
              board1: this.state.boards.board[i],
              imageUrl1: this.state.boards.imageUrl[i],      
              id2: this.state.boards.id[i+1],
              board2: this.state.boards.board[i+1],
              imageUrl2: this.state.boards.imageUrl[i+1],
            });
        }
    }
  };

  handleBoard1Press = () =>
    this.setState((state) => ({
      homePageDisplay: 'none',
      board1PageDisplay: 'block',
      addBoardPageDisplay: 'none',
    }));

  handleBackPress = () =>
    this.setState((state) => ({
      homePageDisplay: 'block',
      board1PageDisplay: 'none',
      addBoardPageDisplay: 'none',
    }));

  handleAddBoardPress = () =>
    this.setState((state) => ({
      homePageDisplay: 'none',
      board1PageDisplay: 'none',
      addBoardPageDisplay: 'block',
    }));

    async getData() {
      //AsyncStorage.removeItem('images'); // Use to remove
      let result = AsyncStorage.getItem('boards', (err, result) => {
        if (result !== null) {
          this.setState({
            boards: JSON.parse(result),
          })
        }
      });
      AsyncStorage.getItem('images', (err, result) => {
        if (result !== null) {
          this.setState({ 
            images: JSON.parse(result), 
          }) 
        }
      });
    }

    componentDidMount() {
       this.getData();
       this.syncBoardstoBoardRow();
    }

  render() {
    return (
      <View style={styles.container}>
        {/*Home PAGE*/}
        <View style={{ display: this.state.homePageDisplay }}>
          <View style={styles.teamsPageView}>
            <View style={styles.topTab}>
              <Text style={styles.title}>IMG-ARCHIVE</Text>
              <View style={styles.addContainer}>
                <TouchableHighlight onPress={this.handleAddBoardPress}>
                  <Image
                    source={{
                      uri:
                        'https://codehs.com/uploads/a61d6279f306d4179aca7fe59bf96bc3',
                    }}
                    style={styles.addIcon}
                  />
                </TouchableHighlight>
              </View>
            </View>
            <ScrollView>
              {this.state.boardRows.map((boardMap) => (
                <View style={styles.boardRowContainer}>
                  <View style={styles.boardLeftContainer}>
                    <TouchableHighlight onPress={this.handleBoard1Press}>
                      <View>
                        <Image
                          source={boardMap.imageUrl}
                          style={styles.boardButton}
                        />
                      </View>
                    </TouchableHighlight>
                    <View>
                      <Text style={styles.boardLabel}>{boardMap.board}</Text>
                    </View>
                  </View>
                  <View style={styles.boardRightContainer}>
                    <TouchableHighlight onPress={this.USDToBitcoin}>
                      <View style={styles.boardButton}></View>
                    </TouchableHighlight>
                    <View>
                      <Text style={styles.boardLabel}>Board 2</Text>
                    </View>
                  </View>
                </View>
              ))}
            </ScrollView>
          </View>
        </View>

        {/*Template Board PAGE*/}
        <View style={{ display: this.state.board1PageDisplay }}>
          <View style={styles.teamsPageView}>
            <View style={styles.topTab}>
              <View style={styles.backContainer}>
                <TouchableHighlight onPress={this.handleBackPress}>
                  <Image
                    source={{
                      uri:
                        'https://codehs.com/uploads/7a886a695fdb890af1e2c2701aa392f2',
                    }}
                    style={styles.backIcon}
                  />
                </TouchableHighlight>
              </View>
              <Text style={styles.title}>All IMGs</Text>
              <View style={styles.menuContainer}>
                <TouchableHighlight onPress={this.handleBackPress}>
                  <Image
                    source={{
                      uri:
                        'https://codehs.com/uploads/ac9b7cf9eaf77f003530cb6ccde22cda',
                    }}
                    style={styles.menuIcon}
                  />
                </TouchableHighlight>
              </View>
            </View>
            <ScrollView>
              <View style={styles.imgRowContainer}>
                <View style={styles.imgLeftContainer}>
                  <TouchableHighlight onPress={this.USDToBitcoin}>
                    <View style={styles.image}></View>
                  </TouchableHighlight>
                </View>
                <View style={styles.imgMidContainer}>
                  <TouchableHighlight onPress={this.USDToBitcoin}>
                    <View style={styles.image}></View>
                  </TouchableHighlight>
                </View>
                <View style={styles.imgRightContainer}>
                  <TouchableHighlight onPress={this.USDToBitcoin}>
                    <View style={styles.image}></View>
                  </TouchableHighlight>
                </View>
              </View>
            </ScrollView>
          </View>
        </View>

        {/*Add Board PAGE*/}
        <View style={{ display: this.state.addBoardPageDisplay }}>
          <View style={styles.teamsPageView}>
            <View style={styles.topTab}>
              <View style={styles.backContainer}>
                <TouchableHighlight onPress={this.handleBackPress}>
                  <Image
                    source={{
                      uri:
                        'https://codehs.com/uploads/7a886a695fdb890af1e2c2701aa392f2',
                    }}
                    style={styles.backIcon}
                  />
                </TouchableHighlight>
              </View>
              <Text style={styles.title}>Add Board</Text>
            </View>
            <ScrollView>
              <View>
                <TextInput
                  value={this.state.newBoardName}
                  onChangeText={(newBoardName) =>
                    this.setState({ newBoardName })
                  }
                  style={styles.textInput}
                  placeholder="Name of New Board"
                  placeholderTextColor="Black"
                />
              </View>
              <View>
                <TextInput
                  value={this.state.newBoardImage}
                  onChangeText={(newBoardImage) =>
                    this.setState({ newBoardImage })
                  }
                  style={styles.textInput}
                  placeholder="Cover Image URL"
                  placeholderTextColor="Black"
                />
              </View>
              <View style={styles.submitBoardButton}>
                <TouchableHighlight onPress={this.addBoard}>
                  <Text style={styles.submitBoardText}>Add Board</Text>
                </TouchableHighlight>
              </View>
            </ScrollView>
          </View>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    fontFamily: 'Helvetica',
    backgroundColor: '#000000',
  },
  topTab: {
    height: (2 * deviceHeight) / 20,
    width: deviceWidth,
    paddingTop: (1.5 * deviceHeight) / 40,
    flexDirection: 'row',
    justifyContent: 'center', //Centered vertically
    alignItems: 'center', // Centered horizontally
    color: '#ffffff',
  },
  teamsPageView: {
    height: deviceHeight,
    width: deviceWidth,
    alignItems: 'center',
    backgroundColor: 'white',
  },
  title: {
    color: 'black',
    fontSize: 20,
    alignSelf: 'center',
    marginBottom: 10,
    top: 10,
  },
  imgLeftContainer: {
    flexDirection: 'column',
  },
  imgMidContainer: {
    flexDirection: 'column',
    marginLeft: 2.5,
  },
  imgRightContainer: {
    flexDirection: 'column',
    marginLeft: 2.5,
  },
  imgRowContainer: {
    marginTop: 5,
    flexDirection: 'row',
  },
  image: {
    flexDirection: 'row',
    height: 7 * (deviceWidth / 20) - 0.5 * (deviceHeight / 20),
    width: 7 * (deviceWidth / 20) - 0.5 * (deviceHeight / 20),
    fontSize: 0.5 * (deviceHeight / 20),
    backgroundColor: '#1c1c1e',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: (0.1 * deviceWidth) / 10,
  },
  boardLeftContainer: {
    flexDirection: 'column',
  },
  boardRightContainer: {
    flexDirection: 'column',
    marginLeft: 10,
  },
  boardRowContainer: {
    marginTop: 5,
    flexDirection: 'row',
  },
  boardButton: {
    flexDirection: 'row',
    height: 10 * (deviceWidth / 20) - 0.5 * (deviceHeight / 20),
    width: 10 * (deviceWidth / 20) - 0.5 * (deviceHeight / 20),
    fontSize: 0.5 * (deviceHeight / 20),
    backgroundColor: '#1c1c1e',
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: (0.25 * deviceWidth) / 10,
  },
  boardLabel: {
    color: 'black',
    fontSize: 1 * (deviceWidth / 20),
    textAlign: 'center',
    fontWeight: '600',
    fontFamily: 'Helvetica Neue',
  },
  backIcon: {
    position: 'absolute',
    height: (1.5 * deviceWidth) / 20,
    width: (1.5 * deviceWidth) / 20,
  },
  backContainer: {
    left: 20,
    top: 45,
    position: 'absolute',
    height: (1.5 * deviceWidth) / 20,
    width: (1.5 * deviceWidth) / 20,
  },
  addIcon: {
    position: 'absolute',
    height: (1.5 * deviceWidth) / 20,
    width: (1.5 * deviceWidth) / 20,
  },
  addContainer: {
    right: 20,
    top: 45,
    position: 'absolute',
    height: (1.5 * deviceWidth) / 20,
    width: (1.5 * deviceWidth) / 20,
  },
  menuIcon: {
    position: 'absolute',
    height: (1 * deviceWidth) / 20,
    width: (1 * deviceWidth) / 20,
  },
  menuContainer: {
    right: 20,
    top: 50,
    position: 'absolute',
    height: (1.5 * deviceWidth) / 20,
    width: (1.5 * deviceWidth) / 20,
  },
  textInput: {
    width: deviceWidth - 60,
    height: 40,
    padding: 8,
    margin: 10,
    backgroundColor: 'White',
    borderBottomWidth: 2,
    borderBottomColor: 'black',
  },
  submitBoardButton: {
    borderWidth: 2,
    borderRadius: 10,
    width: deviceWidth * 0.5,
    height: 45,
    margin: 5,
    textAlign: 'center',
    alignItems: 'center',
    justifyContent: 'center',
  },
  submitBoardText: {
    color: 'black',
    fontSize: 20,
    alignSelf: 'center',
    marginBottom: 10,
    top: 10,
    margin: 5,
    fontFamily: 'Helvetica',
    textAlign: 'center',
  },
});

Upvotes: 1

Views: 1353

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42188

As Emile said, using splice is not the appropriate way to update state. It is safe to add to an array with concat or with [...array, newItem]. Both of those return a new array and leave the original unchanged.

It looks like you are adding a new board based on the state properties newBoardName and newBoardImage and then clearing those properties. So try this:

this.setState((prevState) => ({
  boards: prevState.boards.concat({
    id: prevState.boards.length + 1,
    board: prevState.newBoardName,
    imageUrl: prevState.newBoardImage,
  }),
  newBoardImage: '',
  newBoardName: '',
}));

I'm not sure if I'm understanding your data flow properly, but I think that what you want to do is have the syncBoardstoBoardRow function run after the Promise from getData has resolved:

componentDidMount() {
  this.getData().then(() => this.syncBoardstoBoardRow());
}

In order for getData to resolve only when it actually has the data, you need to await the storage functions. It seems like the "empty" value for result is undefined rather than null, but the easy thing is to just see if result is truthy.


async getData() {
  await AsyncStorage.getItem('boards', (err, result) => {
    if (result) {
      this.setState({
        boards: JSON.parse(result),
      })
    }
  });
  await AsyncStorage.getItem('images', (err, result) => {
    if (result) {
      this.setState({ 
        images: JSON.parse(result), 
      }) 
    }
  });
}

Note that this will run them in sequence rather than in parallel because you await one before starting the next. You can run them in parallel with Promise.all.

async getData() {
  await Promise.all([
    AsyncStorage.getItem('boards', (err, result) => {
      if (result) {
        this.setState({
          boards: JSON.parse(result),
        });
      }
    }),
    AsyncStorage.getItem('images', (err, result) => {
      if (result) {
        this.setState({
          images: JSON.parse(result),
        });
      }
    }),
  ]);
}

You should be using import AsyncStorage from "@react-native-community/async-storage" since AsyncStorage has been deprecated from core.

Upvotes: 1

Related Questions