Reactjs push route after dispatch - reactjs

Hello I am having trouble finding where I can call a route with push after authenticating the user
I did it using redux
my action:
const AuthenticateUser = (login, password) => {
    return dispatch => {
        dispatch (startAuth ());
        // fetching data
        api.post ('/ login', {login, password})
            .then (response => {
            localStorage.setItem ('token', response.data [0]);
            dispatch (userAuthenticated (response.data [1]))
        })
            .catch (err => {console.error (err);});
    }
}
export default AuthenticateUser;
my reducer:
const authReducer = (state = initialState, action) => {
    switch (action.type) {
        case USER_AUTHENTICATED:
            return {
                ... state,
                loading: false,
                authenticated: true,
                user: action.user,
            }
        case USER_FAILED_AUTH:
            return {
                ... state,
                loading: false,
                message: action.error
            }
        default:
        return state;
    }
}
and my form
const SignIn = (props) => {
const classes = useStyles();
const dispatch = useDispatch();
const [login, setLogin] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e) => {
e.preventDefault()
dispatch(auth(login, password))
}
return (
<div className={classes.root}>
<Grid container spacing={2} className={classes.gridMain}>
<Grid item lg={12} md={12} sm={12} xs={12} align="center">
<img src={require("../nodejs-icon.svg")} alt="bug" height={100} />
</Grid>
<Grid item lg={12} md={12} sm={12} xs={12} className={classes.TextField}>
<form onSubmit={handleSubmit}>
<TextField
className={classes.input2}
id="demo2"
label="Usuário"
variant="outlined"
value={login}
onChange={(e) => setLogin(e.target.value)}
InputLabelProps={{
classes: {
root: classes.label,
focused: classes.focusedLabel,
error: classes.erroredLabel
}
}}
InputProps={{
classes: {
root: classes.cssOutlinedInput,
focused: classes.cssFocused,
notchedOutline: classes.notchedOutline,
},
startAdornment: (
<InputAdornment position="start">
<PersonSharpIcon style={{ fontSize: 25, color: 'rgba(20, 176, 12,0.9)' }} />
</InputAdornment>
)
}}
/>
<TextField
className={classes.txtFd}
id="demo2"
label="Senha"
variant="outlined"
value={password}
onChange={(e) => setPassword(e.target.value)}
InputLabelProps={{
classes: {
root: classes.label,
focused: classes.focusedLabel,
error: classes.erroredLabel
}
}}
InputProps={{
classes: {
root: classes.cssOutlinedInput,
focused: classes.cssFocused,
notchedOutline: classes.notchedOutline,
},
startAdornment: (
<InputAdornment position="start">
<LockSharpIcon style={{ fontSize: 25, color: 'rgba(20, 176, 12,0.9)' }} />
</InputAdornment>
)
}}
/>
<ButtonBase variant="raised" disableFocusRipple="false" disableRipple="false" centerRipple="false">
<Typography noWrap className={classes.labelForgot} variant="subtitle2">
Esqueci minha senha
</Typography>
</ButtonBase>
<Button type="submit" className={classes.button} variant="raised" disableFocusRipple="false" disableRipple="false" centerRipple="false">
Entrar
</Button>
</form>
</Grid>
I have a route and after I authenticate this user I wanted to send it to a route or display an error msg, but I don't know where in the code to do it and how I would get it.
my route
const AppRouter = () => (
<BrowserRouter>
<Route path="/xd" component={AuthPage} exact={true} />
<Route path="/dashboard/addProduct" component={AddProduct} exact={true} />
</BrowserRouter>
);

So this is in order to give you an answer that is readable instead of using the comments.
In your SignIn component:
import { withRouter } from "react-router-dom";
const SignIn = (props) => {
const { history } = props;
const { push } = history;
// more variables
const handleSubmit = (e) => {
e.preventDefault()
// You pass the push function over here.
dispatch(auth(login, password, push))
}
// Rest of the logic
}
export default withRouter(SignIn);
The withRouter would give you all the props that are coming from react-router, this will allow you to use the history object, which contains the push function, to redirect the user to the page you want.
Then in your authenticate function which I'm guessing is the ´AuthenticateUser´ function you could do this:
const AuthenticateUser = (login, password, push) => {
return dispatch => {
dispatch (startAuth ());
// fetching data
api.post ('/ login', {login, password})
.then (response => {
localStorage.setItem ('token', response.data [0]);
dispatch (userAuthenticated (response.data [1]))
// push goes here
push("/your-awesome-route")
})
.catch (err => {console.error (err);});
}
}
export default AuthenticateUser;

You may use useEffect in SignIn component, which checks for flag authenticated: true (where this variable is passed via props) and does the redirection.
Something like that
useEffect(() => {
const { authenticated, navigation } = props;
if (authenticated) {
navigation.navigate(Routes.GoodPlace);
}
});

Related

how to upload an array of images to cloudinary using axios and react hook form using next js

here in this code I am able to upload only a single image and upload it to cloudinary api and it works fine, the PROBLEM is it keeps only sending only a single image even though i've specified multiple in the input tag and it loops after it has the event target files and append them to the formData, while what I want is to upload as many images as I want and I have the mongodb model image field as an array. so backend is very solid and doesn't have a problem. the issue is on the front end not being able to send an array of images. it only sends one. i have removed the react hook form validation just incase to see if it works
import axios from 'axios';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { useEffect, useReducer, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-toastify';
import { getError } from '../../utils/error';
import { tokens } from '../../utils/theme';
import {
Grid,
Box,
List,
ListItem,
Typography,
Card,
Button,
ListItemText,
TextField,
useTheme,
CircularProgress,
FormControlLabel,
Checkbox,
} from '#mui/material';
import Header from '../../components/Header';
import Topbar from '../../components/global/Topbar';
function reducer(state, action) {
switch (action.type) {
case 'FETCH_REQUEST':
return { ...state, loading: true, error: '' };
case 'FETCH_SUCCESS':
return { ...state, loading: false, error: '' };
case 'FETCH_FAIL':
return { ...state, loading: false, error: action.payload };
case 'UPDATE_REQUEST':
return { ...state, loadingUpdate: true, errorUpdate: '' };
case 'UPDATE_SUCCESS':
return { ...state, loadingUpdate: false, errorUpdate: '' };
case 'UPDATE_FAIL':
return { ...state, loadingUpdate: false, errorUpdate: action.payload };
case 'UPLOAD_REQUEST':
return { ...state, loadingUpload: true, errorUpload: '' };
case 'UPLOAD_SUCCESS':
return {
...state,
loadingUpload: false,
errorUpload: '',
};
case 'UPLOAD_FAIL':
return { ...state, loadingUpload: false, errorUpload: action.payload };
default:
return state;
}
}
export default function AdminProductEditScreen() {
const theme = useTheme();
const colors = tokens(theme.palette.mode);
const [isFeatured, setIsFeatured] = useState(false);
const [inStock, setInStock] = useState(false);
const [imports, setImports] = useState(false);
const [exports, setExports] = useState(false);
const [image, setImage] = useState([]);
const [category, setCategory] = useState([]);
const { query } = useRouter();
const productId = query.id;
const [
{ loading, error, loadingUpdate, loadingUpload }
dispatch,
] = useReducer(reducer, {
loading: true,
error: '',
});
const {
register,
handleSubmit,
control,
formState: { errors },
setValue,
} = useForm();
useEffect(() => {
const fetchData = async () => {
try {
dispatch({ type: 'FETCH_REQUEST' });
const { data } = await axios.get(`/api/admin/products/${productId}`);
dispatch({ type: 'FETCH_SUCCESS' });
setValue('name', data.name);
setValue('slug', data.slug);
setValue('headerTitle', data.headerTitle);
setImports(data.imports);
setExports(data.exports);
setValue('image', data.image);
} catch (err) {
dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
}
};
fetchData();
}, [productId, setValue]);
const router = useRouter();
const uploadHandler = async (e, imageField = 'image') => {
const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`;
try {
dispatch({ type: 'UPLOAD_REQUEST' });
const {
data: { signature, timestamp },
} = await axios('/api/admin/cloudinary-sign');
const files = Array.from(e.target.files);
const formData = new FormData();
files.forEach((file) => {
formData.append('file', file);
});
formData.append('signature', signature);
formData.append('timestamp', timestamp);
formData.append('api_key', process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY);
const { data } = await axios.post(url, formData);
dispatch({ type: 'UPLOAD_SUCCESS' });
setValue(imageField, data.secure_url);
toast.success('File uploaded successfully');
} catch (err) {
dispatch({ type: 'UPLOAD_FAIL', payload: getError(err) });
toast.error(getError(err));
}
};
const updateCategory = (e) => {
const cats = e.target.files.split(',');
cats?.map((cat) => {
console.log(cat);
setCategory(cat);
});
};
const submitHandler = async ({
name,
slug,
headerTitle,
category,
price,
image,
}) => {
try {
dispatch({ type: 'UPDATE_REQUEST' });
await axios.put(`/api/admin/products/${productId}`, {
name,
slug,
headerTitle,
imports,
exports,
category,
image,
});
dispatch({ type: 'UPDATE_SUCCESS' });
toast.success('Product updated successfully');
router.push('/products');
} catch (err) {
dispatch({ type: 'UPDATE_FAIL', payload: getError(err) });
toast.error(getError(err));
}
};
return (
<Box title={`Edit Product ${productId}`} m="20px">
<Box display="flex" justifyContent="space-between">
<Header title="Product Edit" subtitle="List of Product to be edited" />
<Topbar />
</Box>
{loading && <CircularProgress></CircularProgress> ? (
<Box>Loading...</Box>
) : error ? (
<Box color="error">{error}</Box>
) : (
<ListItem width="100%">
<form onSubmit={handleSubmit(submitHandler)}>
<List sx={{ width: '60vw', border: '2px solid red' }}>
<ListItem>
<Controller
name="name"
control={control}
defaultValue=""
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="name"
label="Name"
error={Boolean(errors.name)}
helperText={errors.name ? 'Name is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="slug"
control={control}
defaultValue=""
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="slug"
label="Slug"
error={Boolean(errors.slug)}
helperText={errors.slug ? 'Slug is required' : ''}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="headerTitle"
control={control}
defaultValue=""
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="headerTitle"
label="header Title"
error={Boolean(errors.headerTitle)}
helperText={
errors.headerTitle ? 'headertitle is required' : ''
}
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<FormControlLabel
label="Import"
control={
<Checkbox
onClick={(e) => setImports(e.target.checked)}
checked={imports}
name="imports"
/>
}
></FormControlLabel>
</ListItem>
<ListItem>
<FormControlLabel
label="Export"
control={
<Checkbox
onClick={(e) => setExports(e.target.checked)}
checked={exports}
name="exports"
/>
}
></FormControlLabel>
</ListItem>
<ListItem>
<Controller
name="price"
control={control}
defaultValue=""
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="price"
label="Price"
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Controller
name="image"
control={control}
defaultValue=""
rules={{
required: true,
}}
render={({ field }) => (
<TextField
variant="outlined"
fullWidth
id="image"
label="Image"
{...field}
></TextField>
)}
></Controller>
</ListItem>
<ListItem>
<Button variant="contained" component="label">
Upload File
<input
type="file"
onChange={(e) => uploadHandler(e)}
hidden
multiple
/>
</Button>
{loadingUpload && <CircularProgress className="mx-4" />}
</ListItem>
{/** BUTTON */}
<ListItem>
<Button
variant="contained"
type="submit"
fullWidth
color="primary"
>
Update
</Button>
{loadingUpdate && <CircularProgress />}
</ListItem>
</List>
</form>
</ListItem>
)}
</Box>
);
}
AdminProductEditScreen.auth = { adminOnly: true };
i have tried in the uploadHandler function to loop through the e.target.files but only keeps sending a single image to the cloudinary api and its uploaded there and i can see it, even though i have selected multiple image files
It looks like you're trying to send a single request to Cloudinary's API, where that single request has multiple values for file.
That's not supported by the Cloudinary API - each call to the API should specify a single file: https://cloudinary.com/documentation/image_upload_api_reference#upload_required_parameters
You should change your code so that when you loop through the array of files, you make a separate call to the API for each one, then continue after all files are uploaded.

Commerce JS API rejecting my test transaction

I'm having a tricky problem. I've created this ecommerce platform with react and I'm using the commerce JS API. I connected my sandbox stripe API key with the sandbox commerce JS key so I could do a test transaction, but each time I get an error and the transaction doesn't go through, leaving my cart quantity unchanged.
In console I get the following error from the commerce JS API:
According to the commerce JS developer setting logs this is the request I'm sending:
{
"line_items": [
{
"id": "item_7RyWOwmK5nEa2V",
"product_id": "prod_LvJjoPJGale0nO",
"name": "Ferrari",
"product_name": "Ferrari",
"media": {
"type": "image",
"source": "https://cdn.chec.io/merchants/28537/assets/o0wPb0EDWAlJnEV1|1.jpg"
},
"sku": null,
"permalink": "kKwz9a",
"quantity": 1,
"price": {
"raw": 1.5,
"formatted": "1.50",
"formatted_with_symbol": "£1.50",
"formatted_with_code": "1.50 GBP"
},
"line_total": {
"raw": 1.5,
"formatted": "1.50",
"formatted_with_symbol": "£1.50",
"formatted_with_code": "1.50 GBP"
},
"is_valid": true,
"product_meta": [],
"tax": {
"is_taxable": false,
"taxable_amount": null,
"amount": null,
"breakdown": null
},
"selected_options": [],
"variant": []
}
],
"customer": [],
"shipping": {
"name": "Primary",
"county_state": "WSM",
"country": "GB"
},
"fulfillment": {
"shipping_method": "ship_RqEv5xvDqwZz4j"
},
"payment": {
"gateway": "stripe",
"stripe": {
"payment_method_id": "pm_1J1FitHjVwDyRBqGNRnw8Ryt"
}
}
}
I don't understand how the email field, shipping address and shipping town/city are missing, as this is what my submission form looks like:
I filled in everything accurately.
Here is my AddressForm component:
import React, { useState, useEffect } from "react";
import {
InputLabel,
Select,
MenuItem,
Button,
Grid,
Typography,
} from "#material-ui/core";
import { useForm, FormProvider } from "react-hook-form";
import { Link } from "react-router-dom";
import { commerce } from "../../lib/commerce";
import FormInput from "./CustomTextField";
const AddressForm = ({ checkoutToken, next }) => {
const [shippingCountries, setShippingCountries] = useState([]);
const [shippingCountry, setShippingCountry] = useState("");
const [shippingSubdivisions, setShippingSubdivisions] = useState([]);
const [shippingSubdivision, setShippingSubdivision] = useState("");
const [shippingOptions, setShippingOptions] = useState([]);
const [shippingOption, setShippingOption] = useState("");
const methods = useForm();
const fetchShippingCountries = async (checkoutTokenId) => {
const { countries } = await commerce.services.localeListShippingCountries(
checkoutTokenId
);
setShippingCountries(countries);
setShippingCountry(Object.keys(countries)[0]);
};
const fetchSubdivisions = async (countryCode) => {
const { subdivisions } = await commerce.services.localeListSubdivisions(
countryCode
);
setShippingSubdivisions(subdivisions);
setShippingSubdivision(Object.keys(subdivisions)[0]);
};
const fetchShippingOptions = async (
checkoutTokenId,
country,
region = null
) => {
const options = await commerce.checkout.getShippingOptions(
checkoutTokenId,
{ country, region }
);
setShippingOptions(options);
setShippingOption(options[0].id);
};
useEffect(() => {
fetchShippingCountries(checkoutToken.id);
}, []);
useEffect(() => {
if (shippingCountry) fetchSubdivisions(shippingCountry);
}, [shippingCountry]);
useEffect(() => {
if (shippingSubdivision)
fetchShippingOptions(
checkoutToken.id,
shippingCountry,
shippingSubdivision
);
}, [shippingSubdivision]);
return (
<>
<Typography variant="h6" gutterBottom>
Shipping address
</Typography>
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit((data) =>
next({
...data,
shippingCountry,
shippingSubdivision,
shippingOption,
})
)}
>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<FormInput required name="firstName" label="First name" />{" "}
</Grid>
<Grid item xs={12} sm={6}>
{" "}
<FormInput required name="lastName" label="Last name" />
</Grid>
<Grid item xs={12} sm={6}>
{" "}
<FormInput required name="address1" label="Address line 1" />
</Grid>
<Grid item xs={12} sm={6}>
{" "}
<FormInput required name="email" label="Email" />
</Grid>
<Grid item xs={12} sm={6}>
{" "}
<FormInput required name="city" label="City" />
</Grid>
<Grid item xs={12} sm={6}>
{" "}
<FormInput required name="zip" label="Zip / Postal code" />
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Country</InputLabel>
<Select
value={shippingCountry}
fullWidth
onChange={(e) => setShippingCountry(e.target.value)}
>
{Object.entries(shippingCountries)
.map(([code, name]) => ({ id: code, label: name }))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Subdivision</InputLabel>
<Select
value={shippingSubdivision}
fullWidth
onChange={(e) => setShippingSubdivision(e.target.value)}
>
{Object.entries(shippingSubdivisions)
.map(([code, name]) => ({ id: code, label: name }))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Options</InputLabel>
<Select
value={shippingOption}
fullWidth
onChange={(e) => setShippingOption(e.target.value)}
>
{shippingOptions
.map((sO) => ({
id: sO.id,
label: `${sO.description} - (${sO.price.formatted_with_symbol})`,
}))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
</Grid>
<br />
<div style={{ display: "flex", justifyContent: "space-between" }}>
<Button component={Link} variant="outlined" to="/cart">
Back to Cart
</Button>
<Button type="submit" variant="contained" color="primary">
Next
</Button>
</div>
</form>
</FormProvider>
</>
);
};
export default AddressForm;
My payment form component:
import React from "react";
import { Typography, Button, Divider } from "#material-ui/core";
import {
Elements,
CardElement,
ElementsConsumer,
} from "#stripe/react-stripe-js";
import { loadStripe } from "#stripe/stripe-js";
import Review from "./Review";
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY);
const PaymentForm = ({
checkoutToken,
nextStep,
backStep,
shippingData,
onCaptureCheckout,
}) => {
const handleSubmit = async (event, elements, stripe) => {
event.preventDefault();
if (!stripe || !elements) return;
const cardElement = elements.getElement(CardElement);
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: cardElement,
});
if (error) {
console.log("[error]", error);
} else {
const orderData = {
line_items: checkoutToken.live.line_items,
customer: {
firstname: shippingData.firstName,
lastname: shippingData.lastName,
email: shippingData.email,
},
shipping: {
name: "Primary",
street: shippingData.address1,
town_city: shippingData.city,
county_state: shippingData.shippingSubdivision,
postal_zip_code: shippingData.zip,
country: shippingData.shippingCountry,
},
fulfillment: { shipping_method: shippingData.shippingOption },
payment: {
gateway: "stripe",
stripe: {
payment_method_id: paymentMethod.id,
},
},
};
onCaptureCheckout(checkoutToken.id, orderData);
nextStep();
}
};
return (
<>
<Review checkoutToken={checkoutToken} />
<Divider />
<Typography variant="h6" gutterBottom style={{ margin: "20px 0" }}>
Payment method
</Typography>
<Elements stripe={stripePromise}>
<ElementsConsumer>
{({ elements, stripe }) => (
<form onSubmit={(e) => handleSubmit(e, elements, stripe)}>
<CardElement />
<br /> <br />
<div style={{ display: "flex", justifyContent: "space-between" }}>
<Button variant="outlined" onClick={backStep}>
Back
</Button>
<Button
type="submit"
variant="contained"
disabled={!stripe}
color="primary"
>
Pay {checkoutToken.live.subtotal.formatted_with_symbol}
</Button>
</div>
</form>
)}
</ElementsConsumer>
</Elements>
</>
);
};
export default PaymentForm;
And also my APP JS:
import React, { useState, useEffect } from "react";
import { Products, Navbar, Cart, Checkout } from "./components";
import { commerce } from "./lib/commerce";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const App = () => {
const [products, setProducts] = useState([]);
const [cart, setCart] = useState({});
const [order, setOrder] = useState({});
const [errorMessage, setErrorMessage] = useState("");
const fetchProducts = async () => {
const { data } = await commerce.products.list();
setProducts(data);
};
const fetchCart = async () => {
setCart(await commerce.cart.retrieve());
};
const handleAddToCart = async (productId, quantity) => {
const { cart } = await commerce.cart.add(productId, quantity);
setCart(cart);
};
const handleUpdateCartQty = async (productId, quantity) => {
const { cart } = await commerce.cart.update(productId, { quantity });
setCart(cart);
};
const handleRemoveFromCart = async (productId) => {
const { cart } = await commerce.cart.remove(productId);
setCart(cart);
};
const handleEmptyCart = async () => {
const { cart } = await commerce.cart.empty();
setCart(cart);
};
const refreshCart = async () => {
const newCart = await commerce.cart.refresh();
console.log(newCart);
setCart(newCart);
};
const handleCaptureCheckout = async (checkoutTokenId, newOrder) => {
try {
const incomingOrder = await commerce.checkout.capture(
checkoutTokenId,
newOrder
);
setOrder(incomingOrder);
refreshCart();
} catch (error) {
setErrorMessage(error.data.error.message);
}
};
useEffect(() => {
fetchProducts();
fetchCart();
}, []);
return (
<Router>
<div>
<Navbar totalItems={cart.total_items} />
<Switch>
<Route exact path="/">
<Products products={products} onAddToCart={handleAddToCart} />
</Route>
<Route exact path="/cart">
<Cart
cart={cart}
handleUpdateCartQty={handleUpdateCartQty}
handleRemoveFromCart={handleRemoveFromCart}
handleEmptyCart={handleEmptyCart}
/>
</Route>
<Route exact path="/checkout">
<Checkout
cart={cart}
order={order}
onCaptureCheckout={handleCaptureCheckout}
error={errorMessage}
/>
</Route>
</Switch>
</div>
</Router>
);
};
export default App;
I spoke to some people on the Commerce JS slack and they pointed out that these fields were 'missing' but I don't understand what they mean by that. I've read that others have had this issue too (specifically people following this tutorial: https://www.youtube.com/watch?v=377AQ0y6LPA) so I'm not sure if it's a problem with the code or the API.
I'm at a loss here so I'd really appreciate any help or suggestions.
Thanks
Console log ur shipping data and check if the names of the objects are the same
for example if : shippingData has the email stored in Email instead of email.So Payment Form cannot recognize the information
in your FormInput component check that you use render instead of as props and try to spread param of callback inside ex:
<Controller control={control}
name={name}
render={({field})=>(<TextField
label={label}
{...field}
required={required}
fullWidth
value={field.value}
/>)}
/>

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/

Converting Class Component to Functional Component with Redux

I have this code and wanted to convert to Functional Component. I've tried with setState and when I click login, it just reloads the login page.
class Login extends Component {
state = {
login: '',
password: '',
keepLogged: false
};
changeValue = event => {
event.preventDefault();
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
};
checkedValue = event => {
console.log(event.target.checked, event.target.name);
const value = event.target.checked;
const name = event.target.name;
this.setState({
[name]: value
});
};
loginUser = () => {
const { login, password } = this.state;
const { requestSignIn } = this.props;
requestSignIn({ login, password });
};
render() {
const { login, password } = this.state;
return (
<LoginWrapper>
<Containe}>
<Row>
<Col>
<LoginForm>
<FormControl
name='login'
type='text'
placeholder='Username/Email'
value={login}
onChange={this.changeValue}
style={{ marginBottom: '10px' }}
/>
<FormControl
name='password'
type='password'
placeholder='Password'
value={password}
onChange={this.changeValue}
style={{ marginBottom: '10px' }}
/>
<Button
variant='info'
value='Log In'
onClick={() => this.loginUser()}
>
Log In
</Button>
</LoginForm>
</Col>
</Row>
<Row>
<Col>
<LoginBottom
onClick={() => history.push('/registration')}
style={{ marginTop: '30px' }}
>
Need an account?{' '}
<Link style={{ color: '#007bff' }}>Sign Up</Link>
</LoginBottom>
</Col>
</Row>
</Container>
</LoginWrapper>
);
}
}
const mapDispatchToProps = dispatch => ({
requestSignIn: data => dispatch(requestSignIn(data))
});
export default connect(null, mapDispatchToProps)(Login);
This is how far I am right now. And, it does not work.
function Login(props) {
const classes = useStyles();
const [inputs, setInputs] = useState({
// you can login with email or username
login: '',
password: '',
keepLogged: false
});
const { login, password } = inputs;
function handleChange(e) {
event.preventDefault();
const { name, value } = e.target;
setInputs(inputs => ({ ...inputs, [name]: value }));
}
function handleCheck(e) {
console.log(e.target.checked, e.target.name);
const { name, value } = e.target.check;
setInputs(inputs => ({ ...inputs, [name]: value }));
}
const loginUser = () => {
const { login, password } = this.state;
const { requestSignIn } = this.props;
requestSignIn({ setInputs });
};
return (
<LoginWrapper>
<Container>
<Row>
<Col>
<LoginForm>
<FormControl
name='login'
type='text'
placeholder='Username/Email'
value={login}
onChange={handleChange}
/>
<FormControl
name='password'
type='password'
placeholder='Password'
value={password}
onChange={handleChange}
/>
<Button
variant='info'
value='Log In'
onClick={() => this.loginUser()}
>
Log In
</Button>
</LoginForm>
</Col>
</Row>
<Row>
<Col>
<LoginBottom
onClick={() => history.push('/registration')}
style={{ marginTop: '30px' }}
>
Need an account?{' '}
<Link style={{ color: '#007bff' }}>Sign Up</Link>
</LoginBottom>
</Col>
</Row>
</Container>
</LoginWrapper>
);
}
const mapDispatchToProps = dispatch => ({
requestSignIn: data => dispatch(requestSignIn(data))
});
export default connect(null, mapDispatchToProps)(Login);
I am not quite sure, how I can convert this part to functional component :/ How can I convert this to functional component over here?
loginUser = () => {
const { login, password } = this.state;
const { requestSignIn } = this.props;
requestSignIn({ login, password });
};
render() {
const { login, password } = this.state;
You have this local variable for your state:
const [inputs, setInputs] = useState({
// you can login with email or username
login: '',
password: '',
keepLogged: false
});
And your props are on the local variable props.
So all you need to do is use those local variables:
const loginUser = () => {
const { login, password } = inputs;
const { requestSignIn } = props;
requestSignIn({ login, password });
};

React getting Maximum update depth exceeded error

I'm performing a change password, for authError I'm getting the following error..
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
changepassword.js
import React, { Component } from 'react'
import withStyles from "#material-ui/core/styles/withStyles";
import { Redirect } from 'react-router-dom'
import IconButton from '#material-ui/core/IconButton';
import { connect } from 'react-redux'
import { compose } from 'redux'
import {changePassword } from '../../store/actions/auth'
const styles = {
textField: {
fontSize: '5px'
},
};
class ChangePassword extends Component {
state = {
loading: false,
open:false,
message:'',
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
openSnackbar = ({ message }) => {
this.setState({
open: true,
message,
});
};
handleSubmit = (e) => {
e.preventDefault();
let curpass=this.state.cp_currentPassword
let newpass=this.state.cp_newPassword
this.setState({loading:true});
this.props.changePassword(curpass,newpass)
this.openSnackbar({ message: 'Password changed Successfully.!' })
}
render() {
const { classes, auth, authError } = this.props;
const { loading } = this.state;
const message = (
<span
id="snackbar-message-id"
dangerouslySetInnerHTML={{ __html: this.state.message }}
/>
);
if (!auth.uid) return <Redirect to='/signin' />
return (
<div>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<CardHeader color="warning">
<h4 className={classes.cardTitleWhite}>Change Password</h4>
</CardHeader>
<form >
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CardBody>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_newPassword"
label="New Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_newPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_confirmPassword"
label="Confirm Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_confirmPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
</GridContainer>
</CardBody>
<CardFooter>
<Button color="warning" onClick={this.handleSubmit} disabled={loading}>
{loading && <CircularProgress style={{ color: 'white', height: '20px', width: '20px', marginRight: '10px' }} />}
Change Password
</Button>
</CardFooter>
</GridItem>
</GridContainer>
</form>
</Card>
</GridItem>
</GridContainer>
{authError ? this.openSnackbar({ message: '{authError}' }) : null}
<Snackbar
open={this.state.open}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
message={message}
variant="error"
onClose={() => this.setState({ open: false, message: '' })}
action={
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => this.setState({ open: false, message: '' })}
>
<CloseIcon className={classes.icon} />
</IconButton>
}
autoHideDuration={3000}
/>
</div>
)
}
}
const mapstateToProps = (state) => {
return {
auth: state.firebase.auth,
authError: state.authroot.autherr
}
}
const mapDispatchtoProps = (dispatch) => {
return {
changePassword: (currentPassword,newPassword) => { dispatch(changePassword(currentPassword,newPassword)) }
}
}
export default compose(
withStyles(styles),
connect(mapstateToProps,mapDispatchtoProps)
)(ChangePassword);
change password action
export const changePassword = (currentPassword, newPassword) => {
return (dispatch, getState, { getFirebase }) => {
const firebase = getFirebase();
console.log(currentPassword);
console.log(newPassword);
var user = firebase.auth().currentUser;
user.updatePassword(newPassword).then(() => {
console.log("Password updated!");
}).catch((error) => {
dispatch({ type: 'CHANGEPASSWORD_ERR', error })});
}
}
You are updating state here
{authError ? this.openSnackbar({ message: '{authError}' }) : null
that line runs multiple times, as it checks if there was auth error, if there was call openSnackBar, openSnackBar update state, which cause the component to re-render, after re-render the check happens again etc, which causes a loop. change it to the following and only call openSnackBar when state.open is false.
{authError && !this.state.open ? this.openSnackbar({ message: '{authError}' }) : null}
EDIT
remove authError from render and check in componentDidMount
componentDidMount = () => {
const { authError } = this.props;
if (authError) {
this.openSnackbar({ message: '{authError}' });
}
};

Resources