React: change state after fetch in functional component - reactjs

I have the following component:
export default function SignIn() {
const [isLoading, setIsLoading] = useState(false);
const classes = useStyles();
const handleSubmit = (event) => {
event.preventDefault();
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={(event) => { handleSubmit(event); setIsLoading(true); }}>
{ isLoading ?
<CircularProgress/> :
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
}
</form>
</div>
</Container>
);
}
When I submit the form, the "isLoading" becomes true, and the loading bars show up. Anyway if the "AuthService.login()" fails, the "isLoading" state is not updated.
What am I doing wrong?
Working code
The problem was related to the fact that
if(!AuthService.login(event.target))
is an async function, so I had to "await" for the response in order to evaluate it's result.
The working code:
async function handleSubmit (event) {
event.preventDefault();
setIsLoading(true)
try {
await AuthService.login(event.target);
setIsLoading(false)
} catch (e) {
console.log('Handle errors here');
} finally {
console.log('We do cleanup here');
}
}

Change:
onSubmit={(event) => { handleSubmit(event); setIsLoading(true); }}
to:
onSubmit={(event) => { handleSubmit(event) }}
Also change handleSubmit to:
const handleSubmit = (event) => {
event.preventDefault();
setLoading(true);
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}

you are always setting isLoading to true in onSubmit
export default function SignIn() {
const [isLoading, setIsLoading] = useState(false);
const classes = useStyles();
const handleSubmit = (event) => {
event.preventDefault();
setLoading(true)
if(!AuthService.login(event.target))
{
setIsLoading(false);
}
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={(event) => { handleSubmit(event) }}>
{ isLoading ?
<CircularProgress/> :
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
}
</form>
</div>
</Container>
);
}

Related

React Hooks form OnSubmit makes Problems

Hey i cant find a solution why my code dosent work. When i trigger the button nothing happens, normally i want to display the data but nothing happend. My Code is in attachment.
if someone has an idde to solve the problem I would be very grateful.
I have looked at other posts but nothing helps with my Problem.
export default function FormDialog(props) {
const [open, setOpen] = React.useState(false);
const {
control,
formState: { errors },
setValue,
} = useFormContext();
let isError = false;
let errorMessage = "";
if (errors && errors.hasOwnProperty(name)) {
isError = true;
errorMessage = errors[name].message;
}
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
props.func();
};
const handleChange = (event) => {
props.setMenge(event.target.value);
};
React.useEffect(() => {
if (errors) {
console.log(errors);
}
}, [errors]);
return (
<>
<Button variant="contained" onClick={handleClickOpen}>
Umbuchen
</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>Umbuchung</DialogTitle>
<DialogContent>
<DialogContentText>
Bitte geben Sie die Menge ein die umgelagert werden soll!
</DialogContentText>
<Controller
control={control}
name={props.name}
render={({ field }) => (
<TextField
{...field}
label={props.label}
error={isError}
helperText={errorMessage}
autoFocus
margin="dense"
id="menge"
type="number"
fullWidth
variant="standard"
/>
)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Schließen</Button>
<Button variant="contained" color="primary" type="submit">
Umbuchen
</Button>
</DialogActions>
</Dialog>
</>
);
}
export default function Home() {
const [sEingang, setSEingang] = useState([]);
const [sLager, setSLager] = useState("");
const [menge, setMenge] = useState(0);
const context = React.useContext(AuthContext);
const { data: warendata } = useWaren(context.access_token);
const { data: lagerdata } = useGetLager(context.access_token);
const methods = useForm({
resolver: yupResolver(validationShema),
mode: "all",
});
const { getValues, reset, watch, handleSubmit } = methods;
const waren = watch("selWaren");
const { data: chargendata, refetch } = useChargen({
access_token: context.access_token,
lager: waren,
});
const { mutateAsync, isError: mutError, isLoading, isSuccess } = useAnlegen();
React.useEffect(() => {
async function getToken() {
const res = await context.setTokenSilently();
}
const interval = setInterval(() => {
getToken();
}, 30000);
return () => clearInterval(interval);
}, []);
const onSubmit = (data) => {
console.log("data: ", data);
};
return (
<>
<CssBaseline />
<ButtonAppBar name={"Wareneingänge"} back={false} onClick={reset} />
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justifyContent="center"
sx={{p: 2, m: 3}}
style={{ minHeight: "50vh", }}
>
<Grid item xs={3}>
<MySelect
formname={"selWaren"}
data={warendata}
name={"Wareneingänge"}
selected={sEingang}
setSelected={setSEingang}
reset={reset} />
</Grid>
<Grid item xs={3}>
<MySelect
formname={"selLager"}
data={lagerdata}
name={"Freie Lagerplätze"}
selected={sLager}
setSelected={setSLager} />
</Grid>
<Grid item xs={3}>
<MySelect
formname={"selChargen"}
data={chargendata}
name={"Freie Chargenplätze"}
selected={sLager}
setSelected={setSLager} />
</Grid>
<Grid item xs={3} sx={{ m: 3}}>
<FormDialog
func={onSubmit}
name={"selMenge"} />
</Grid>
</Grid>
</form>
</FormProvider>
</>
);
}
It looks like you've forgotten to add an onClick handler for the submit button here:
<Button variant="contained" color="primary" type="submit">
Umbuchen
</Button>
Something like this should work:
<Button onClick={() => props.func()} variant="contained" color="primary" type="submit">
Umbuchen
</Button>

'Save Palette' button gives a TypeError of 'savePalette' is not a function

My 'NewPaletteForm' is a functional component as I've used react hooks.
So when I click the button 'Save Palette', an error occurs which says 'props.savePalette is not a function'.
ERROR:-
TypeError: props.savePalette is not a function
Code:
'App.js':
class App extends Component {
constructor(props) {
super(props);
this.state = { palettes: seedColors };
this.findPalette = this.findPalette.bind(this);
this.savePalette = this.savePalette.bind(this);
}
findPalette(id) {
return this.state.palettes.find(function (palette) {
return palette.id === id;
});
}
savePalette(newPalette) {
this.setState({ palettes: [...this.state.palettes, newPalette] });
}
render() {
return (
<Switch>
<Route
exact
path='/palette/new'
render={(routeProps) => <NewPaletteForm savePalette={this.savePalette} {...routeProps} />} />
'NewPaletteForm.js' : Functional Component which includes react hooks
function NewPaletteForm() {
const classes = useStyles();
const [open, setOpen] = useState(false);
const [currentColor, setCurrentColor] = useState('teal');
const [colors, setColors] = useState([{ color: 'pink', name: 'pink' }]);
const [newName, setNewName] = useState('');
useEffect(() => {
ValidatorForm.addValidationRule('isColorNameUnique', (value) => {
return colors.every(
({ name }) => name.toLowerCase() !== value.toLowerCase()
);
});
ValidatorForm.addValidationRule('isColorUnique', (value) => {
return colors.every(
({ color }) => color !== currentColor
);
});
})
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
function updateCurrentColor(newColor) {
setCurrentColor(newColor.hex);
};
function addNewColor() {
const newColor = {
color: currentColor,
name: newName
}
setColors(oldColors => [...oldColors, newColor]);
setNewName('');
};
function handleChange(evt) {
setNewName(evt.target.value);
}
function handleSubmit(props) {
const newPalette = {
paletteName: 'Test Palette',
colors: colors
}
props.savePalette(newPalette);
props.history.push('/');
}
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
color='default'
className={clsx(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Persistent drawer
</Typography>
<Button variant='contained' color='primary' onClick={handleSubmit}>Save Palette</Button>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
<ChevronLeftIcon />
</IconButton>
</div>
<Typography variant='h4'>Design Your Palette</Typography>
<div>
<Button variant="contained" color="secondary">
Clear Palette
</Button>
<Button variant="contained" color="primary">
Random Color
</Button>
</div>
<Divider />
<ChromePicker color={currentColor} onChangeComplete={updateCurrentColor} />
<ValidatorForm onSubmit={addNewColor}>
<TextValidator
value={newName}
onChange={handleChange}
validators={['required', 'isColorNameUnique', 'isColorUnique']}
errorMessages={['Enter a color name', 'Color name must be unique', 'Color already used!']}
/>
<Button
variant='contained'
type='submit'
color='primary'
style={{
backgroundColor: currentColor
}}
>
Add Color
</Button>
</ValidatorForm>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.drawerHeader} />
{colors.map(color => (
<DraggableColorBox color={color.color} name={color.name} />
))}
</main>
</div>
);
}
Button in the NewPaletteForm Component: (Used Material UI Button component)
<Button variant='contained' color='primary' onClick={handleSubmit}>Save Palette</Button>
You should remove props from function argument. because you are calling handleSubmit on button click so it's getting event as props.
function handleSubmit() {
const newPalette = {
paletteName: 'Test Palette',
color: colors
}
props.savePalette(newPalette);
props.history.push('/');
}
Also add props argument to function NewPaletteForm(props) {

firebase Error : FirebaseError: Missing or insufficient permissions

so I'm getting these Errors out of nowhere, I worked with the code yesterday and everything was fine.
This is like a social Media platform and
yesterday I could display the Posts, and today I can't. I can still take pictures and save it in the Firebase DB that works fine but it won't post itself at the feed.
This is the Code:
function Profile(props) {
const classes = useStyles();
const [reason, setReason] = React.useState('');
const [open, setOpen] = React.useState(false);
const handleChange = (event) => {
setReason(event.target.value);
};
const handleClose = () => {
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
};
const [userPosts, setUserPosts] = useState([]);
const [user, setUser] = useState(null);
const [following, setFollowing] = useState(false)
useEffect(() => {
const { currentUser, posts } = props;
console.log({ currentUser, posts });
if (props.route.params.uid === firebase.auth().currentUser.uid) {
setUser(firebase.auth().currentUser);
setUserPosts(posts);
}else{
firebase.firestore()
.collection("users")
.doc(props.route.params.uid)
.get()
.then((snapshot) =>{
if(snapshot.exists){
setUser(snapshot.data())
}else{
console.log('does not exist')
}
})
firebase.firestore()
.collection("posts")
.doc(props.route.params.uid)
.collection("userPosts")
.orderBy("creation", "asc")
.get()
.then((snapshot) =>{
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return{id, ...data}
})
setUserPosts(posts)
})
}
if(props.following.indexOf(props.route.params.uid) > -1){
setFollowing(true);
}else{
setFollowing(false)
}
},[props.route.params.uid, props.following]);
const onFollow = () =>{
firebase.firestore()
.collection("following")
.doc(firebase.auth().currentUser.uid)
.set({
following : [props.route.params.uid]
})
}
const onLogout = () =>{
firebase.auth().signOut();
}
if (user === null) {
return <View />;
}
return (
<div className={classes.div}>
<div >
<Avatar alt="Ana Pädagogin" className={classes.avatar} />
<Typography className={classes.text} > {user.name} </Typography>
<Typography className={classes.text} > {user.email} </Typography>
{props.route.params.uid !== firebase.auth().currentUser.uid ? (
<Container>
{following ? (
<Button
className={classes.btn}
size="large"
variant="outlined"
onClick={() => onUnFollow()}
>Following</Button>
) :
(
<Button
className={classes.btn}
size="large"
variant="outlined"
onClick={() => onFollow()}
>Follow</Button>
)}
</Container>
) : <Button
className={classes.btn}
size="large"
variant="outlined"
onClick={() => onLogout()}
>Logout</Button>}
<Card>
{/* //Verspätung */}
<CardContent>
<Typography variant="h5" className={classes.cardTyp}> Verspätung </Typography>
<Container className={classes.cardContainer}>
<TextField
id="time"
label="Zeit"
type="time"
className={classes.cardTime}
defaultValue="07:30"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
<Button className={classes.cardBtn}>Absenden</Button>
</Container>
</CardContent>
{/* //Krankenmledungen */}
<CardContent className={classes.cardKrankmeldung}>
<Typography variant="h5" className={classes.cardTyp}> Krankenmledungen </Typography>
<Container className={classes.cardContainer}>
<TextField
id="date"
label="Von"
type="date"
defaultValue="2017-05-24"
className={classes.textField}
InputLabelProps={{
shrink: true,
}}
/>
<TextField
id="date"
label="bis"
type="date"
defaultValue="2017-05-24"
className={classes.textField}
InputLabelProps={{
shrink: true,
}}
/>
</Container>
<Button className={classes.cardBtn}>Absenden</Button>
</CardContent>
{/* //Verspätung Abolung*/}
<CardContent>
<Typography variant="h5" className={classes.cardTyp}> Verspätung Abholung</Typography>
<Container className={classes.cardContainer}>
<TextField
id="time"
label="Zeit"
type="time"
defaultValue="07:30"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
/>
<Button className={classes.cardBtn}>Absenden</Button>
</Container>
</CardContent>
</Card>
</div>
</div>
)
}
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts,
following: store.userState.following
});
export default connect(mapStateToProps, null)(Profile);
As Frank van Puffelen pointed out your problem lies in Firebase Firestore security rules.
If you can save post on db, it seems that you have write permission but not read permission on that collection.
You can test how changing security rules affect this problem by first enabling all reads and writes to that collection
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /yourCollectionName/{ID}{
allow read, write: if true;
}
}
}
, and then restricting the collection as you need.
Check these guides to apply needed modifications and best practices to your security rules.
https://firebase.google.com/docs/firestore/security/get-started
https://firebase.google.com/docs/firestore/security/rules-structure

React search by name

Hello I am currently fetching through an API call to search by name in my search bar. I'm having an error of whenever I type a string in my search bar.
This is my postman URL /product/list/search?search=assorted where I can fetch names with "assorted"
I'm fetching it in frontend like this
export const searchProducts = async (query) => {
return await get(`product/list/search=?${query})
}
and this is how I implement it on my search page
const SearchPage = () => {
const [products, setProducts] = useState([])
const [query, setQuery] = useState("")
useEffect(() => {
getAllProducts().then((products) => {
setProducts(products)
})
}, [])
useEffect(() => {
const loadProducts = async () => {
const query = await searchProducts(query)
setQuery(query)
}
loadProducts()
}, [query])
return (
<>
<Grid container spacing={3}>
<Grid item xs={1}>
<Box pt={1.5}>
<Link to="#">
<ArrowBackIcon className={classes.backSize} />
</Link>
</Box>
</Grid>
<Grid item xs={11}>
<Paper className={classes.root}>
<IconButton aria-label="menu"></IconButton>
<InputBase
className={classes.input}
placeholder="Search for foods"
onChange={(e) => setQuery(e.target.value)}
value={query}
/>
<IconButton aria-label="search">
<SearchIcon />
</IconButton>
</Paper>
</Grid>
</Grid>
<Box pt={1}>
<CategoryList categories={categories} />
</Box>
<Box pt={1}>
<ProductList products={products} />
</Box>
</>
)
}
Did you try with / at the beginning?
export const searchProducts = async (query) => {
return await get(`/product/list/search?search=${query}`)
}
From what i can see your postman api call and the one in the await doesn't seems to be matching - it should be
await get(`product/list/search?search=${query}`);

Can't send post request to authenticate user in react & redux

I have implemented all of actions and want to call that action with username and password parameters to authenticate user in form.But when I click submit no request is being sended and nothing happens.Code is a little bit long so I'm gonna cut the main part.
actions.js
const authStart = () => {
return {
type: "AUTH_START",
};
};
const authSucceed = (token) => {
return {
type: "AUTH_SUCCESS",
payload: token,
};
};
const authFailed = (error) => {
return {
type: "AUTH_FAILED",
payload: error,
};
};
export const authLogin = (username, password) => {
return (dispatch) => {
dispatch(authStart());
axios
.post("http://127.0.0.1:8000/rest-auth/login/", {
username: username,
password: password,
})
.then((res) => {
const token = res.data.key;
dispatch(authSucceed(token));
})
.catch((err) => {
dispatch(authFailed(err));
});
};
};
SignIn.js
const SignIn = (props) => {
const classes = useStyles();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
// const [username,setUsername] = useState("")
// const [password,setPassword] = useState("")
const submitHandler = (e) => {
e.preventDefault();
props.onAuth(username,password)
setUsername("");
setPassword("");
};
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form onSubmit={submitHandler} className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="username"
label="Username"
name="username"
autoComplete="username"
autoFocus
onChange={(e) => setUsername(e.target.value)}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={(e) => setPassword(e.target.value)}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link to="/signup" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
</div>
<Box mt={8}>
<Copyright />
</Box>
</Container>
);
};
const mapStateToProps = state => {
return state.Auth
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username,password) => {authLogin(username,password)}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(SignIn);
I'll be grateful if you help me out with this.Thanks
Looking at the way your code is setup, authLogin returns a function which takes dispatch as a param, so you probably need to do:
onAuth: (username,password) => {authLogin(username,password)(dispatch)}
You could use bindActionCreators() as well to return the dispatch function, in the following way:-
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(authLogin(username,password), dispatch)
}
}
If you want another way to do the same, you could refer to the link below.
https://blog.logrocket.com/react-redux-connect-when-and-how-to-use-it-f2a1edab2013/

Resources