I'm working on a project for School and I have some problems with them.
So this is the code
import React from 'react';
import { AppBar, Container, Toolbar, Typography, MenuItem, Select, makeStyles, createTheme, ThemeProvider } from "#material-ui/core";
import { useNavigate } from "react-router-dom";
import { CryptoState } from '../CryptoContext';
//Creeare Design - Titlu
const useStyles = makeStyles(() => ({
title: {
flex:1,
color: "gold",
fontFamily: "Montserrat",
fontWeight: "bold",
cursor: "pointer",
}
}))
const Header = () => {
const classes = useStyles();
const navigate = useNavigate();
const {currency,setCurrency} = CryptoState() || {};
console.log(currency);
// TODO: Gandire sistem reintoarcere pagina principala
const handleClick = () => navigate.push('/'); // nu merge - reintoarcere la pagian principala
// FIXME: Creeare template temă
const darkTheme = createTheme({
palette:{
primary: {
main: "#fff",
},
type: "dark",
},
});
return (
<ThemeProvider theme={darkTheme}>
<AppBar color='transparent' position='static'>
<Container>
<Toolbar>
<Typography onClick={handleClick}
className={classes.title} variant='h5'>Crypto Hunter</Typography>
<Select variant="outlined" style={{
width:100,
height:40,
marginLeft: 15,
}}
value={currency} onChange={(e) => setCurrency(e.target.value)}
>
<MenuItem value={"RON"}>RON</MenuItem>
<MenuItem value={"EUR"}>EUR</MenuItem>
<MenuItem value={"USD"}>USD</MenuItem>
</Select>
</Toolbar>
</Container>
</AppBar>
</ThemeProvider>
)
};
export default Header;
And this is the problem, I don't know how I can fix them.
What I want?
Image here
Please help me!
If I change from select with RON or USD or EURO, i want to console.log to can go to the next step.
But it gives me this error (image), and i don't know how i can fix this
EDIT:
Also, from CryptoState i have this code
const Crypto = createContext();
const CryptoContext = ({children}) => {
const [currency, setCurrency] = useState("RON");
const [symbol, setSymbol] = useState("RON");
useEffect(() => {
if(currency === "RON") setSymbol("RON");
else if(currency === "EUR") setSymbol("€");
else if(currency === "USD") setSymbol("$");
},[currency]);
return <Crypto.Provider value={{currency,symbol,setCurrency}}>{children}</Crypto.Provider>
};
export default CryptoContext;
export const CryptoState = () => {
useContext(Crypto);
}
change that line
const {currency,setCurrency} = CryptoState() || {};
to
const [currency,setCurrency] =useState(CryptoState() || {}) ;
Related
I am implementing a provider which helps me to have the state of my user in different views, the main function of this provider is to render different one or the other stack navigator depending on whether the variable is full or empty, this in order to be able to generate two groups of screens depending on whether the user is authenticated or not.
Here my code:
View router.tsx:
import { NavigationContainer } from "#react-navigation/native"
import React, { useContext, useEffect, useState, useRef } from "react"
import { UserContext } from "./context/Usuario"
import AuthStack from "./routes/AuthStack"
import GeneralStack from "./routes/GeneralStack"
const Router = () => {
const { me } = useContext(UserContext)
const auth = useRef(false)
useEffect(() => {
return () => {
auth.current = me !== null
console.log("Hola")
}
}, [me])
return (
<NavigationContainer>
{auth.current ? <GeneralStack /> : <AuthStack />}
</NavigationContainer>
)
}
export default Router
Provider user,js:
import React, { useEffect, createContext, useState } from "react"
import AsyncStorage from "#react-native-async-storage/async-storage"
export const UserContext = createContext()
const UserProvider = ({ children }) => {
const [me, setMe] = useState(undefined)
const validStorage = async () => {
try {
const miSesion = await AsyncStorage.getItem("sesion")
console.log(miSesion)
setMe(JSON.parse(miSesion))
} catch (error) {
console.log(`ERROR: ${error.message}`)
}
}
useEffect(() => {
validStorage()
}, [])
return (
<UserContext.Provider value={{ me, setMe }}>
{children}
</UserContext.Provider>
)
}
export default UserProvider
GeneralStack:
import { createNativeStackNavigator } from "#react-navigation/native-stack"
import React from "react"
import TabStack from "./TabStack"
//import TabStack from "./TabStack"
const GeneralScreen = createNativeStackNavigator()
const GeneralStack = () => {
return (
<GeneralScreen.Navigator screenOptions={{ headerShown: false }}>
<GeneralScreen.Screen name="Tabs" component={TabStack} />
</GeneralScreen.Navigator>
)
}
export default GeneralStack
AuthStack:
import { createNativeStackNavigator } from "#react-navigation/native-stack"
import React from "react"
import Login from "../Screens/Login"
import Registro from "../Screens/Registro/Registro"
import SplashScreen from "../SplashScreen"
const AuthScreen = createNativeStackNavigator()
const AuthStack = () => {
return (
<AuthScreen.Navigator
initialRouteName="Splash"
screenOptions={{ headerShown: false }}>
<AuthScreen.Screen name="Login" component={Login} />
<AuthScreen.Screen name="Register" component={Registro} />
<AuthScreen.Screen name="Splash" component={SplashScreen} />
</AuthScreen.Navigator>
)
}
export default AuthStack
Login:
import React, { useState, useContext } from "react"
import {
Image,
ScrollView,
StatusBar,
Text,
TouchableOpacity,
View,
} from "react-native"
import { useNavigate } from "../../Hooks/useNavigate"
import MyTextInput from "../../components/MyTextInput"
import colors from "../../styles/colors"
import { loginStyles } from "../../styles/styles"
import { UserContext } from "../../context/Usuario"
import AsyncStorage from "#react-native-async-storage/async-storage"
export default function Login() {
const [, setIsSession] = useState(false)
const { setMe } = useContext(UserContext)
const navigate = useNavigate()
const [hidePassword, sethidePassword] = React.useState(false)
const [user] = useState({ user: "admin", password: "admin123" })
const [form, setForm] = useState({ user: "", password: "" })
const getStorage = async () => {
if (await AsyncStorage.getItem("sesion")) {
setIsSession(true)
} else {
setIsSession(false)
}
}
const signIn = async () => {
try {
console.log(user)
if (form.user === user.user && form.password === user.password) {
await AsyncStorage.setItem("sesion", JSON.stringify(form))
setMe(form)
setIsSession(true)
}
} catch (error) {
console.error(error)
}
}
const closeSesion = async () => {
await AsyncStorage.removeItem("sesion")
getStorage()
}
return (
<ScrollView contentContainerStyle={[loginStyles.container]}>
<StatusBar backgroundColor={colors.PURPLE} translucent={true} />
<View style={loginStyles.logo}>
<Image
source={require("../../recursos/images/Logo.png")}
style={{ height: 250, width: 250 }}
/>
</View>
<MyTextInput
onChangeText={(text: string) => {
setForm(state => ({ ...state, user: text }))
}}
keyboardType="email-address"
placeholder="E-mail"
/>
<MyTextInput
onChangeText={(text: string) => {
setForm(state => ({ ...state, password: text }))
}}
keyboardType={null}
placeholder="Contraseña"
bolGone={true}
secureTextEntry={hidePassword}
onPress={() => sethidePassword(!hidePassword)}
/>
<View style={loginStyles.btnMain}>
<TouchableOpacity onPress={signIn}>
<Text style={loginStyles.btntxt}>Iniciar Sesión</Text>
</TouchableOpacity>
</View>
<View style={loginStyles.btnTransparent}>
<TouchableOpacity
onPress={() => navigate({ screen: "Register" })}>
<Text
style={[loginStyles.btntxt, { color: colors.PURPLE }]}>
Registrarse
</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity>
<Text
style={[
loginStyles.txtTransparent,
{ textDecorationLine: "none" },
]}>
Olvide mi contraseña
</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
Home:
import AsyncStorage from "#react-native-async-storage/async-storage"
import React, { useState } from "react"
import { Image, ScrollView, TouchableOpacity } from "react-native"
import { Calendar } from "react-native-calendars"
import { Text } from "react-native-elements"
import { useNavigate } from "../../Hooks/useNavigate"
import colors from "../../styles/colors"
const Home = () => {
const [showModal, setShowModal] = useState(false)
const [date, setDate] = useState<string>()
const navigate = useNavigate()
const [isSession, setIsSession] = useState(false)
const getStorage = async () => {
const data = await AsyncStorage.getItem("sesion")
console.log(data)
if (data) {
setIsSession(true)
} else {
setIsSession(false)
}
}
const closeSesion = async () => {
await AsyncStorage.removeItem("sesion")
getStorage()
}
return (
<ScrollView>
<TouchableOpacity onPress={() => closeSesion()}>
<Text>Hola porque soy bien molon</Text>
</TouchableOpacity>
<Image
source={require("../../recursos/images/coralio_logo.png")}
style={{
marginTop: 40,
height: 110,
width: "80%",
justifyContent: "center",
alignSelf: "center",
}}
/>
<Calendar
theme={{
selectedDayBackgroundColor: colors.BLACK,
arrowColor: colors.WHITE,
monthTextColor: colors.WHITE,
}}
style={{
backgroundColor: colors.PURPLE,
borderRadius: 10,
elevation: 4,
marginTop: 60,
margin: 10,
height: 400,
}}
onDayPress={day => {
console.log(day.dateString)
setDate(day.dateString)
setShowModal(false)
}}
onMonthChange={() => {}}
initialDate={"2023-01-16"}
minDate={new Date()
.toLocaleDateString("es-US", {
year: "numeric",
month: "2-digit",
day: "numeric",
formatMatcher: "basic",
})
.split("/")
.reverse()
.join("-")}
markedDates={{
day: {
marked: true,
dotColor: colors.WHITE,
selected: true,
selectedColor: colors.PURPLE,
},
}}
//maxDate={"2023-12-31"}
//hideExtraDays={false}
//disableArrowLeft={true}
//disableArrowRight={true}
//hideArrows={true}
//hideDayNames={true}
/>
</ScrollView>
)
}
export default Home
The problem I have is when doing the close session in home, if in login it sends me from the authstack view to generalstack, when I do the close session it doesn't send me back to login, but it does clean the state of the variable from asyncstorage.
Help :(
It looks like you're not clearing the me variable in your context when the user ends the session. I think your closeSesion method should look like this:
const closeSesion = async () => {
setMe(null)
await AsyncStorage.removeItem("sesion")
getStorage()
}
When trying to create a simple quiz app without the need to prop drilling I've stumbled upon an issue while trying to integrate context into the project. The issue is that when subscribing to my context as shown below and console. logging 'name', I get the value of undefined. What am I missing in order to get my name(stored in a state in my context) logged instead of getting undefined?
My context
import React, { createContext, Dispatch, SetStateAction, useContext, useState } from 'react';
export interface IUserContextType {
name: string;
test: string;
setName: Dispatch<SetStateAction<string>>;
setTest: Dispatch<SetStateAction<string>>;
}
type IQuizContextProvidorProps = {
children: React.ReactNode;
};
export const QuizContext = createContext({} as IUserContextType);
export const useQuizContext = () => useContext(QuizContext);
const QuizContexProvider = ({ children }: IQuizContextProvidorProps) => {
const [name, setName] = useState('Marvin');
const [test, setTest] = useState('This is a test');
const values = { name, test, setName, setTest };
return <QuizContext.Provider value={values}>{children}</QuizContext.Provider>;
};
export default QuizContexProvider;
My App
import { useState } from 'react';
import './App.css';
import quizApi from './utils/quiz.json';
import { IQuiz, IQuizAnswers } from './model/IQuiz';
import { Button, LinearProgress, Paper, styled, Typography } from '#mui/material';
import { Box } from '#mui/system';
import QuizContexProvider, { useQuizContext } from './utils/QuizContex';
const QuizContainer = styled(Box)(({ theme }) => ({
'.correct': {
backgroundColor: 'darkseagreen',
},
'.linearProgress': {
height: '1rem',
},
}));
function App() {
const { name, test } = useQuizContext();
console.log('name', name);
function shuffle(array: Array<any>) {
return array.sort(() => Math.random() - 0.5);
}
const quiz: Array<IQuiz> = shuffle(quizApi);
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const [progress, setProgress] = useState(0);
const [viewQuiz, setViewQuiz] = useState(true);
const [quizScore, setQuizScore] = useState(0);
const inkrementWith = 100 / quiz.length;
const handleProgress = () => {
setProgress(progress + inkrementWith);
};
const handleAnswer = (answers: IQuizAnswers) => {
const nextQuestion = currentQuestionIndex + 1;
handleProgress();
if (nextQuestion < quiz.length) {
setCurrentQuestionIndex(nextQuestion);
} else {
setViewQuiz(false);
}
if (answers.isTrue === true) {
setQuizScore(quizScore + 1);
}
};
const handleReset = () => {
setCurrentQuestionIndex(0);
setProgress(0);
setQuizScore(0);
setViewQuiz(true);
};
return (
<QuizContexProvider>
<QuizContainer className='App'>
<Box component='header' className='App-header'>
{viewQuiz ? (
<>
<Box sx={{ width: '50%' }}>
<LinearProgress className='linearProgress' variant='determinate' color='success' value={progress} />
</Box>
{quiz.map(
(question, index) =>
index === currentQuestionIndex && (
<Box key={index}>
<Box>{question.questionLabel}</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', margin: '1rem' }}>
{shuffle(question.answerOptions).map((answers, index) => (
<Paper
key={index}
onClick={() => {
return handleAnswer(answers);
}}
component='button'
>
{answers.answerLabel}
</Paper>
))}
</Box>
</Box>
)
)}
</>
) : (
<Paper>
<Typography component='h1' variant='h3'>
Quiz results
</Typography>
<Typography component='h2' variant='subtitle1'>
Quiz results
</Typography>
<Typography component='h1' variant='h1' sx={{ fontWeight: 700 }}>
{quizScore} / {quiz.length}
</Typography>
<Button variant='contained' onClick={handleReset} sx={{ margin: '1rem 0rem' }}>
Reset quiz
</Button>
</Paper>
)}
</Box>
</QuizContainer>
</QuizContexProvider>
);
}
export default App;
Any component that wish to use context value, should be wrapped inside the provider. Your <App /> component is using context value, so it should be:
<QuizContexProvider>
<App />
</QuizContexProvider>
You can put the provider in Index.ts file.
I have figured out how to pull the notifications from the database but having trouble creating a filter that allows the user that asked the question to only get notifications when their questions have been answered.
The code I wrote is getting an unmounted error on line 31 with how I'm performing the filter. Here is the error message:
Can't perform a React state update on an unmounted component. This is
a no-op, but it indicates a memory leak in your application. To fix,
cancel all subscriptions and asynchronous tasks in a useEffect cleanup
function.
Here is the code:
import React, { useEffect, useState } from "react";
import { makeStyles } from "#material-ui/core/styles";
import Popper from "#material-ui/core/Popper";
import NotificationsIcon from "#material-ui/icons/Notifications";
import "../Style/Header.css";
import db, { auth } from "../firebase";
const useStyles = makeStyles((theme) => ({
paper: {
border: "1px solid",
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
zIndex: "10",
},
}));
export default function SimplePopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const [notifications, setNotifications] = useState([]);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popper" : undefined;
useEffect(() => {
let mounted = true;
db.collection("notifications")
.where(auth.askerUserId, "==", auth.currentUser.uid)
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
if (mounted) {
setNotifications(
snapshot.docs.map((doc) => ({
id: doc.id,
content: doc.data().content,
}))
);
}
});
return () => (mounted = false);
}, []);
return (
<div className="header__icon">
<NotificationsIcon
aria-describedby={id}
type="button"
onClick={handleClick}
/>
<Popper id={id} open={open} anchorEl={anchorEl} style={{ zIndex: 100 }}>
<div className={classes.paper}>
<ul className="notifications">
{notifications.map((notification) => (
<li key={notification.id}>{notification.content}</li>
))}
</ul>
</div>
</Popper>
</div>
);
}
You should unsubscribe when the component gets unmounted:
import React, { useEffect, useState } from "react";
import { makeStyles } from "#material-ui/core/styles";
import Popper from "#material-ui/core/Popper";
import NotificationsIcon from "#material-ui/icons/Notifications";
import "../Style/Header.css";
import db, { auth } from "../firebase";
const useStyles = makeStyles((theme) => ({
paper: {
border: "1px solid",
padding: theme.spacing(1),
backgroundColor: theme.palette.background.paper,
zIndex: "10",
},
}));
export default function SimplePopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
const [notifications, setNotifications] = useState([]);
const handleClick = (event) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popper" : undefined;
useEffect(() => {
let mounted = true;
let unsub=db.collection("notifications")
.where(auth.askerUserId, "==", auth.currentUser.uid)
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
if (mounted) {
setNotifications(
snapshot.docs.map((doc) => ({
id: doc.id,
content: doc.data().content,
}))
);
}
});
return () => {unsub()};
}, []);
return (
<div className="header__icon">
<NotificationsIcon
aria-describedby={id}
type="button"
onClick={handleClick}
/>
<Popper id={id} open={open} anchorEl={anchorEl} style={{ zIndex: 100 }}>
<div className={classes.paper}>
<ul className="notifications">
{notifications.map((notification) => (
<li key={notification.id}>{notification.content}</li>
))}
</ul>
</div>
</Popper>
</div>
);
}
I am new to this so I hope this is the right place to get help!
As titled, executing this code is giving me the "Too many re-renders" error on React.
I have tried going through all lines and checking my hooks repeatedly, but nothing seems to work.
I am guessing this is happening due to useEffect, so pasting the code for the relevant components below:
UseResults:
import { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async () => {
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term,
location: 'san francisco'
}
});
setResults(response.data.businesses);
} catch (err) {
setErrorMessage('Something went wrong')
}
};
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, results, errorMessage];
}
SearchScreen:
import React, { useState } from 'react';
import { Text, StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import ResultsList from '../components/ResultsList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = (navigation) => {
const [term, setTerm] = useState('');
const [searchApi, results, errorMessage] = useResults();
const filterResultsByPrice = (price) => {
return results.filter(result => {
return result.price === price;
});
};
return <>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={searchApi()}
/>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text>We have found {results.length} results</Text>
<ScrollView>
<ResultsList
results={filterResultsByPrice('$')}
title="Cost Effective"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$')}
title="Bit Pricier"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$$')}
title="Big Spender"
navigation={navigation}
/>
</ScrollView>
</>
};
const styles = StyleSheet.create({});
export default SearchScreen;
ResultsList:
import React from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
const ResultsList = ({ title, results, navigation }) => {
return (
<View style={styles.container} >
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('ResultsShow')}>
<ResultsDetail result={item} />
</TouchableOpacity>
)
}}
/>
</View>
);
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5
},
container: {
marginBottom: 10
}
});
export default ResultsList;
TIA!
I have a project where i can have n-number of input field. After when i add 200 items, it starts lagging after that. Demo : https://testcreate.vivekneel.now.sh/create (To test: Focus last input and try pressing enter button to create new input)
Source Code. : https://github.com/VivekNeel/Create-Test
This is my main container :
import React, { useState } from "react";
import CreateTerms from "./CreateTerms";
import { initTerms, contructTermObject } from "./utils";
import { Container } from "#material-ui/core/";
const CreateStudySetContainer = () => {
const [terms, setTerms] = useState(initTerms);
const [inputIdToFocus, setInputIdToFocus] = useState(null);
const handleCreateTerm = () => {
const newTerm = contructTermObject(terms.length + 1, 2);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
setTerms(newTerms);
};
console.log("....rendering");
return (
<Container maxWidth={"md"}>
<CreateTerms
terms={terms}
inputIdToFocus={inputIdToFocus}
createTerm={handleCreateTerm}
/>
;
</Container>
);
};
export default CreateStudySetContainer;
This the CreateTerms code :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles, Card } from "#material-ui/core/";
import ContentItemRow from "./ContentItemRow";
const styles = () => ({
card: {
marginBottom: 16,
},
});
const CreateTerms = (props) => {
const { terms, classes, createTerm, inputIdToFocus } = props;
return (
<div className={classes.container}>
{terms.map(({ node: { term } }, index) => {
return (
<Card key={term.id} className={classes.card}>
<p>{index}</p>
<ContentItemRow
autoFocus={term.id === inputIdToFocus}
createTerm={createTerm}
term={term}
/>
;
</Card>
);
})}
</div>
);
};
export default withStyles(styles)(CreateTerms);
This is ContentItemRow :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles } from "#material-ui/core/";
import ContentEditor from "./ContentEditor";
const styles = {
container: {
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
},
term: {
marginRight: 16,
flex: 1,
},
facts: {
flex: 1,
},
};
const ContentItemRow = (props) => {
const { term, classes, createTerm, autoFocus } = props;
return (
<div className={classes.container}>
<div className={classes.term}>
<ContentEditor
autoFocus={autoFocus}
createTerm={createTerm}
placeholder={"New term"}
/>
</div>
<div className={classes.facts}>
{term.facts.map(({ fact }) => {
return (
<ContentEditor
key={fact.id}
createTerm={createTerm}
placeholder={"New fact"}
/>
);
})}
</div>
</div>
);
};
export default withStyles(styles)(ContentItemRow);
This is ContentEditor :
import React from "react";
import { TextField } from "#material-ui/core/";
const ContentEditor = (props) => {
const { placeholder, createTerm, autoFocus } = props;
const handleOnKeyDown = (event) => {
const { keyCode } = event;
if (keyCode === 13) {
createTerm();
}
};
return (
<TextField
onKeyDown={handleOnKeyDown}
fullWidth
autoFocus={autoFocus}
placeholder={placeholder}
/>
);
};
export default ContentEditor;
When debugging, I noticed that dom updates only the last div which gets added. I don't know where the lagging is coming from.
Not sure if this will work but you can try the following:
const ContentItemRow = React.memo(function ContentItemRow (props) => {
And to prevent re creating the create term handler:
const handleCreateTerm = useCallback(() => {
setTerms((terms) => {
const newTerm = contructTermObject(
terms.length + 1,
2
);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
return newTerms;
});
}, []);