So, I am currently working on a program that is supposed to be a mock-up of a Student Educational Plan for a school. The basic gist of it is that it displays a home page with buttons to a form to submit information (text input has buttons to submit or clear the information entered into the to an AsyncStorage database and a page that displays reports of information collected from the form. All pages display somewhat normally, but for some reason, when I press the submit button, the page freezes and does not submit the info. Here is the code I have as of right now. I am stuck trying to figure out how to use AsyncStorage in the context, which is why I don't have it added here. Another thing to add is that the names in the StyleSheet are the way they are is because I had originally based the code from a previous program (mock-up Music Player) I had worked on. Please explain everything as simply as possible.
Edit: I have tried to do more with the AsyncStorage, but the buttons don't work properly. Below is the edited version of the previous code.
import { Image, ScrollView, StyleSheet, Text, View, Button, TextInput,} from 'react-native';
//import {AsyncStorage} from '#react-native-async-storage/async-storage';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="SEP Form" component={SEPScreen} />
<Stack.Screen name="SEP Database" component={SEPInfo} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Button
title="Your Student Educational Plan"
onPress={() =>
navigation.navigate('SEP Form')
}
/>
<Button
title="All Student Educational Plans"
onPress={() =>
navigation.navigate('SEP Database')
}
/>
</View>
);
};
const SEPScreen = ({ navigation, route }) => {
const[SEPInfo, setForm] = useState({});
const save = async() => {
try{
await AsyncStorage.setItem(SEPInfo)
if (SEPInfo.name == null || SEPInfo.name == '') {
alert("Please Enter your Name");
}
else if (SEPInfo.id == null || SEPInfo.id == '') {
alert("Please Enter your ID");
}
else if (SEPInfo.major == null || SEPInfo.major == '') {
alert("Please Enter your Major");
}
else if (SEPInfo.goal == null || SEPInfo.goal == '') {
alert("Please Enter your Goal");
}
else {
await AsyncStorage.setItem("form", JSON.stringify(SEPInfo));
alert(`Form Saved! Name: ${SEPInfo.name}\nID: ${SEPInfo.id}\nMajor: ${SEPInfo.major}\nGoal: ${SEPInfo.goal}`)
}
}
catch (error) {
alert(error);
};
/* const clear = async() => {
try{
await AsyncStorage.removeItem(SEPInfo);
setForm({});
alert('Form Cleared');
}
catch (error){
alert(error);
}
}; */
const load = async () => {
try {
let form = JSON.parse (await AsyncStorage.getItem(form));
if (form !== null) {
setForm(SEPInfo);
}
}
catch (error) {
alert(error);
}
};
}
function clear () {
setState({name:'', id:'', major:'', goal:''});
alert('Form has been cleared!');
}
return (
<View style={styles.container}>
<Text style={styles.sectionTitle}>Enter your info here:</Text>
<TextInput style={styles.input} name="name" label="StudentName " placeholder="Name" onChangeText={(text) => setForm({...SEPInfo, name: text})} />
<TextInput style={styles.input} name="id" label="StudentID " placeholder="ID" onChangeNumber={(number) => setForm({...SEPInfo, id: number})} />
<TextInput style={styles.input} name="major" label="Major " placeholder="Major" onChangeText={(text) => setForm({...SEPInfo, major: text})} />
<TextInput style={styles.input} name="goal" label="Goal " placeholder="Goal" onChangeText={(text) => setForm({...SEPInfo, goal: text})} />
<Button title="Submit"
onPress={() => save()
}
></Button>
<Button title="Clear"
onPress={clear}
></Button>
</View>
);
};
const SEPInfo = ({navigation, route}) => {
return(
<View style={styles.container}>
<Text style={styles.sectionTitle}>SEP Database</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#CFEAF1'
},
listWrapper: {paddingTop: 80, paddingHorizontal: 20},
sectionTitle: {fontWeight: 'bold', fontSize: 30, margin: 5, color: '#2E2090', alignSelf: 'center'},
sectionTitleTwo: {fontWeight: 'bold', fontSize: 25, margin: 5, color: '#206290', alignSelf: 'center'},
flexContainerWaiting: {backgroundColor: '#20BD57', padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 5, marginBottom: 5, justifyContent: 'space-evenly'},
flexContainerOne: {backgroundColor: '#0B97B0', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
flexContainerTwo: {backgroundColor: '#D773EB', fontSize: 16, flex: 0.5, padding: 15, borderRadius: 10, flexDirection: 'row', alignItems: 'center', marginTop: 2.5, marginBottom: 2.5, justifyContent: 'space-evenly'},
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10,
},
});
export default MyStack;
Looking at the AsyncStorage docs (https://reactnative.dev/docs/asyncstorage#setitem), it says that when using setItem(), you need to pass a key: string and a value: string. In your code, you only seem to pass a value: AsyncStorage.setItem(TextInput). Furthermore, you pass the value TextInput into setItem(), but you don't seem to define TextInput anywhere. What you should do is use the value prop and onChangeText prop to store the value of the TextInput in a state variable, as is shown on the docs: https://reactnative.dev/docs/textinput. Then, pass that state variable into setItem() as they value and pass another item as its key.
for saving an item in async storage:-
AsyncStorage.setItem('usertoken', token);
for fetching an item from async storage:-
// need to be called inside an async function
let token = await AsyncStorage.getItem('userToken');
Related
Disclaimer: I am new to JS so this problem is probably pretty simple to solve, but I have consulted all resources and stack overflow posts I could find online on the subject
This is probably something simple that's missing but I haven't been able to debug this problem :
I want to connect an Azure Function API to a React app, but I am unable to process the Html Response, which makes my await functions hang.
Here is the very simple Azure functions API code :
import logging
import azure.functions as func
import json
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
return func.HttpResponse(
json.dumps({
'recommendations': [1,2,3,4,5]
}),
mimetype="application/json"
);
And here is my React based app (running with Expo) :
class App extends Component {
state = {
selectedUser: 0,
step: 0,
recommendations: 1
};
goToStep0 = async () => {
this.setState({ step: 0, recommendations: 1 });
};
goToStep1 = async () => {
const { data: rec } = await axios.get(config.API_URL);
//const rec = await axios.get(config.API_URL);
// const options = {method: "GET"};
// var rec;
// fetch(config.API_URL,options)
// .then(data => {rec = data.recommendations;});
this.setState({ step: 1, recommendations: rec });
};
The Azure function is receiving input and returns a successful answer everytime.
What I have tried :
Changing the the Azure function app to return a string
Trying to get the request with fetch
Removing async and running a more old school request :
var rec;
fetch(config.API_URL)
.then(response => response.json())
.then(data => {
rec = data
});
Probably at least 10 other things I have forgotten (I have wasted more than 4 hours on this...)
Thanks a lot in advance for your help!
Edit : Below is the code for the rendering part of the app (text is in French) :
render() {
if (!config.API_URL) {
return (
<View style={styles.container}>
<Image
style={{ resizeMode: "center", width: 450, height: 150 }}
source={require("./assets/icon-flat.png")}
/>
<Text style={{ color: "red", margin: 20 }}>
L'app n'est pas configurée correctement. Mettez à jour le fichier
config.json comme indiqué dans le README.
</Text>
</View>
);
}
return (
<View style={styles.container}>
<Image
style={{ resizeMode: "center", width: 450, height: 150 }}
source={require("./assets/icon-flat.png")}
/>
<Text style={{ fontSize: 18, padding: 20, textAlign: "center" }}>
Choisissez votre profil afin de recevoir des recommendations de
lecture personnalisées : {this.state.recommendations.toString()}
Quel est mon état : {this.state.step}
</Text>
<Picker
style={{ height: 200, width: "80%", margin: 30 }}
selectedValue={this.state.selectedUser}
onValueChange={value => this.setState({ selectedUser: value })}
>
{[...Array(10000).keys()].map(e => (
<Picker.Item key={e} label={`User ${e}`} value={e} />
))}
</Picker>
<Button title="Se connecter" onPress={this.goToStep1} />
</View>
);
}
}
I have managed to find the problem. The code was not problematic as much as the fact that I was using an old version of react and axios.
With the newest version, I am unable to send requests to localhost but I managed to get responses after deploying the application.
I have "deployed" my Azure function API to the internet using Ngrok which allows me to handle requests.
You can call the callback function on Axios call like this:
axios.get(config.API_URL)
.then(res => {
if (res) {
console.log("Data fetched: ", res)
// do stuff
}
else {
// do stuff
}
}).catch(err => {
console.log(err)
})
Like you did with fetch
The Problem
I'm fetching the data from an API and I want to show the options with title, images and a button when the user clicks its redirect to the post page
Fetching Code
const [posts, setPosts] = useState([]);
const { search } = useLocation();
useEffect(() => {
const fetchPosts = async () => {
const res = await axios.get("/posts" + search);
setPosts(res.data);
};
fetchPosts();
}, [search]);
Material UI Autocomplete Input Code
return(
<Stack spacing={2} sx={{ width: 300 }}>
<Autocomplete
freeSolo
id="free-solo-2-demo"
getOptionLabel={(posts) => `${posts.title}`}
disableClearable
options={posts}
noOptionsText={"Not Found"}
sx={{ width: "100%", margin: "10px", background: "#fff" }}
renderInput={(params) => (
<TextField
{...params}
label="Search..."
InputProps={{
...params.InputProps,
type: 'search',
}}
/>
)}
/>
</Stack>
);
I tried
adding ${post.photo} to the code but didn't work, it showed the image path (Name) next to the title only
getOptionLabel={(posts) => `${posts.title} ${posts.photo}`}
My question is: can I view images and clickable buttons to Material UI Autocomplete options? if yes how? if no what other options do I have?
I have defined two screens in my App.js.
import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import HomeScreen from './HomeScreen';
import FAQScreen from './FAQScreen';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="HomeScreen">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="FAQ" component={FAQScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
In my HomeScreen.js I want to simply press a button and navigate to FAQScreen.
However, I keep getting an error:
The action 'NAVIGATE' with payload {"name":FAQScreen}" was not handled by any navigator.
Do you have a screen named 'FAQScreen'?
I tried to do multiple Google search and found no solution to solve this.
Here is my HomeScreen.js for completeness.
mport 'react-native-gesture-handler';
import React, { Component } from 'react';
import { StyleSheet, Text, SafeAreaView, View, Button} from 'react-native';
class MyButtonText extends Component {
render() {
var phoneHeight = 500;
const myFontSize = phoneHeight/25;
return (
<Text style={{ fontFamily: "Courier New", fontSize: myFontSize }} onPress = {() => console.log("text clicked")}>
{this.props.children}
</Text>
);
}
}
class MyInfoText extends Component {
render() {
var phoneHeight = 500;
const myFontSize = phoneHeight/30;
return (
<Text numberOfLines = {2} style={{ fontFamily: "Courier New", fontSize: myFontSize, alignItems: "center", textAlign: "center"}}>
{this.props.children}
</Text>
);
}
}
function HomeScreen({ navigation }) {
const toolKaiserID = "alkjakljf";
navigateToScreen=()=>{
console.log('Going to FAQ screen now...');
navigation.navigate('FAQScreen')
}
return (
// this is a comment
<SafeAreaView style={styles.container}>
{/* top bar */}
<View style = {topBarStyle.container}>
<Button onPress={this.navigateToScreen} title="Information" />
<MyButtonText>ID: {toolKaiserID}</MyButtonText>
<Button title="Feedback"/>
</View>
{/* info bar */}
<View style = {infoBarStyle.container}>
<MyInfoText>paper on floor -- tool(s) on top; take photos from at least 3ft/1m away</MyInfoText>
</View>
{/* camera view */}
<View style = {cameraStyle.container}></View>
{/* bottom bar */}
<View style = {bottomBarStyle.container}>
<Button title="1 Photos"/>
<Button title="2 Check"/>
<Button title="3 Editor"/>
<Button title="4 Paper"/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#434343',
},
});
const viewStyle = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'blue',
},
});
const topBarStyle = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: 'space-around',
backgroundColor: 'blue',
},
});
const infoBarStyle = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
});
const cameraStyle = StyleSheet.create({
container: {
flex: 5,
backgroundColor: 'red',
},
});
const bottomBarStyle = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: 'space-around',
backgroundColor: 'yellow',
},
});
export default HomeScreen;
There is no FAQScreen name in your stack navigator
<Stack.Screen name="FAQ" component={FAQScreen} />
When you use navigate, it's need the name, that is "FAQ"
If you want to navigate using name "FAQScreen", you can set like this:
<Stack.Screen name="FAQScreen" component={FAQScreen} />
How do I make Materia-UI GridList Face vertically in One Row on Mobile?
My GridList is two in a row which is what I wanted but when I inspect it on mobile it still shows two in a row instead of One. that means it's not responsive, How do I make it responsive?
this is my Events Component.
import React from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import GridList from "#material-ui/core/GridList";
import GridListTile from "#material-ui/core/GridListTile";
import GridListTileBar from "#material-ui/core/GridListTileBar";
import ListSubheader from "#material-ui/core/ListSubheader";
mport IconButton from "#material-ui/core/IconButton";
import Button from "#material-ui/core/Button";
import { withStyles } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
const useStyles = (theme) => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
},
gridList: {
width: 1000,
height: 950,
},
icon: {
color: "rgba(255, 255, 255, 0.54)",
},
});
class EventsList extends React.Component {
constructor(props) {
super(props);
this.state = { events: [] };
}
componentDidMount() {
axios
.get("http://localhost:9000/events/")
.then((response) => {
this.setState({ events: response.data });
})
.catch(function (error) {
console.log(error);
});
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<GridList cellHeight={330} className={classes.gridList} spacing={8}>
<GridListTile key="Subheader" cols={2} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{this.state.events.map((tile) => (
<GridListTile key={tile.eventImage}>
<img src={tile.eventImage} alt={tile.title} />
<GridListTileBar title={tile.title} titlePosition="top" />
<GridListTileBar
paragraph={tile.description}
actionIcon={
<IconButton
aria-label={`info about ${tile.title}`}
className={classes.icon}
></IconButton>
}
/>
</GridListTile>
))}
</GridList>
</div>
);
}
}
export default withStyles(useStyles)(EventsList);
this is the List of my events displayed by GridList on Mobile
but something like this is what I want to see when I am on Mobile.
Since you are using class based component, you can use withWidth to conditionally set cols value to GridList.
See working example here in codesandbox
Example code snippet
class TitlebarGridList extends Component {
render() {
const { classes, width } = this.props; // <---- see here
let columns = width === "xs" || width === "sm" ? 1 : 2;
console.log("=========>", width);
return (
<div className={classes.root}>
<GridList cellHeight={200} className={classes.gridList} cols={columns}> {/* //<---- see here */}
<GridListTile key="Subheader" cols={3} style={{ height: "auto" }}>
<ListSubheader component="div">December</ListSubheader>
</GridListTile>
{tileData.map(tile => (
<GridListTile key={tile.title}>
<img className={classes.image} src={tile.img} alt={tile.title} />
</GridListTile>
))}
</GridList>
</div>
);
}
}
TitlebarGridList.propTypes = {
classes: PropTypes.object.isRequired
};
export default compose(
withStyles(styles, {
name: "TitlebarGridList"
}),
withWidth() //<---- see here
)(TitlebarGridList);
Note: withWidth will be deprecated sometime in the future, so consider to use useMediaQuery hook (in that case you have to switch to functional component).
I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.