Reputation: 63
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
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