首页 > 解决方案 > React Native - 如何在 ASYNC 函数完成后执行函数

问题描述

所以我正在开发这个 React 原生图像存储应用程序。该应用程序的想法是能够将大量图像存储到分类板上。现在我正在处理电路板部分,我希望应用程序存储电路板。现在的问题是我有这个功能,它存储板的行而不是单个板,而是它们的一行。此函数称为“syncBoardstoBoardRow”。

我遇到的问题是,每当我调用此函数时,它都是在我的 AsyncStorage Asynchronus “getItems”函数之后,该函数获取每个板的持久 JSON 文件,因此我的 syncBoardstoBoardRow 函数没有板可以使用,因为它之前的函数仍然没有t 完成了所有的董事会。

有谁知道如何让一个函数等待执行,直到前一个异步函数完成它的工作?这最好在我的 componentDidMount 函数中。

重要代码:

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();
    }

所有代码:

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',
  },
});

标签: reactjsreact-native

解决方案


正如 Emile 所说,使用splice不是更新状态的适当方式。concat使用或 with添加到数组中是安全的[...array, newItem]。两者都返回一个新数组并保持原始数组不变。

看起来您正在根据状态属性添加一个新板newBoardNamenewBoardImage然后清除这些属性。所以试试这个:

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

我不确定我是否正确理解了您的数据流,但我认为您想要做的是在from解决syncBoardstoBoardRow后运行该函数:PromisegetData

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

为了getData只在实际有数据时才解析,需要await存储功能。看起来resultisundefined而不是的“空”值null,但简单的事情就是看看是否result


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), 
      }) 
    }
  });
}

请注意,这将按顺序而不是并行运行它们,因为您await在开始下一个之前先运行它们。您可以将它们与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),
        });
      }
    }),
  ]);
}

您应该使用import AsyncStorage from "@react-native-community/async-storage",因为AsyncStorage已从核心中弃用。


推荐阅读