I would to add geolocation.getCurrentPosition in my code below using react and redux and then send the coordinates (Lat and Long) to the Local Storage but i don't have any idea !
I deleted the HTML parts of the form (city, postalCode .. to avoid repetition
Anyone can help me with please
import React, { useState } from 'react';
import { Form, Button } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import FormContainer from '../components/FormContainer';
import CheckoutSteps from '../components/CheckoutSteps';
import { saveShippingAddress } from '../actions/cartActions';
const ShippingScreen = ({ history }) => {
const cart = useSelector((state) => state.cart);
const { shippingAddress } = cart;
const [address, setAddress] = useState(shippingAddress.address);
const [city, setCity] = useState(shippingAddress.city);
const [postalCode, setPostalCode] = useState(shippingAddress.postalCode);
const [country, setCountry] = useState(shippingAddress.country);
const dispatch = useDispatch();
const submitHandler = (e) => {
e.preventDefault();
dispatch(
saveShippingAddress({ address, city, postalCode, country })
);
history.push('/payment');
};
return (
<FormContainer>
<CheckoutSteps step1 step2 />
<h1>Shipping</h1>
<Form onSubmit={submitHandler}>
<Form.Group controlId="address">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="Enter address"
value={address}
required
onChange={(e) => setAddress(e.target.value)}
></Form.Control>
</Form.Group>
<Button type="submit" variant="primary">
Continue
</Button>
</Form>
</FormContainer>
);
};
export default ShippingScreen;
Related
I am dispatching saveShippingAddress from ShippingScreen.js and in redux devtools it shows saveShippingAddress being dispatched but the it's functionality in shippingAddressSlice.js is not working. I have tried to add other actions in reducers but they are also not working
ShippingScreen.js
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Form, Button } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import FormContainer from '../components/FormContainer';
import { saveShippingAddress } from '../features/shippingAddressSlice';
const ShippingScreen = () => {
const cart = useSelector((store) => store.cart);
const { shippingAddress } = cart;
console.log(shippingAddress);
const navigate = useNavigate();
const dispatch = useDispatch();
const [address, setAddress] = useState(shippingAddress.address);
const [city, setCity] = useState(shippingAddress.city);
const [postalCode, setPostalCode] = useState(shippingAddress.country);
const [country, setCountry] = useState(shippingAddress.country);
const submitHandler = (e) => {
e.preventDefault();
dispatch(saveShippingAddress({ address, city, postalCode, country }));
navigate('/payment');
};
return (
<FormContainer>
<h1>Shipping</h1>
<Form onSubmit={submitHandler}>
<Form.Group controlId='address'>
<Form.Label>Address</Form.Label>
<Form.Control
type='address'
placeholder='Enter Address'
value={address ? address : ''}
required
onChange={(e) => setAddress(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId='city'>
<Form.Label>City</Form.Label>
<Form.Control
type='city'
placeholder='Enter City'
value={city ? city : ''}
required
onChange={(e) => setCity(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId='postalCode'>
<Form.Label>postalcode</Form.Label>
<Form.Control
type='text'
placeholder='Enter postalcode'
value={postalCode ? postalCode : ''}
required
onChange={(e) => setPostalCode(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId='country'>
<Form.Label>Country</Form.Label>
<Form.Control
type='text'
placeholder='Enter Country name'
value={country ? country : ''}
required
onChange={(e) => setCountry(e.target.value)}
></Form.Control>
</Form.Group>
<Button variant='primary' type='submit'>
Continue
</Button>
</Form>
</FormContainer>
);
};
export default ShippingScreen;
shippingAddressSlice.js
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
cartItems: [],
shippingAddress: {},
};
const shippingAddressSlice = createSlice({
name: 'ShippingAddress',
initialState,
reducers: {
saveShippingAddress: (state, action) => {
console.log('hello');
console.log(action.payload);
// localStorage.setItem('shippingAdrress', JSON.stringify(action.payload));
return {
...state,
shippingAddress: action.payload,
};
},
},
});
console.log(shippingAddressSlice);
export const { saveShippingAddress, closeModal } = shippingAddressSlice.actions;
export default shippingAddressSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
import productListReducer from './features/productListFeature/productListSlice';
import productDetailsReducer from './features/productListFeature/productDetailSlice';
import CartReducer from './features/addToCart/cartSlice';
import userLoginReducer from './features/UserFeature/loginUserSlice';
import userRegisterReducer from './features/UserFeature/registerUserSlice';
import userDetailsReducer from './features/UserFeature/userDetailsSlice';
import userUpdateProfileReducer from './features/UserFeature/updateProfileSlice';
import ShippingAddressReducer from './features/shippingAddressSlice';
const cartItemsFromStorage = localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [];
const userInfoFromStorage = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null;
const shippingAddressFromStorage = localStorage.getItem('shippingAddress')
? JSON.parse(localStorage.getItem('shippingAddress'))
: {};
const initialState = {
cart: {
cartItems: cartItemsFromStorage,
shippingAddress: shippingAddressFromStorage,
},
userLogin: {
userInfo: userInfoFromStorage,
},
};
const store = configureStore({
reducer: {
productList: productListReducer,
productDetails: productDetailsReducer,
cart: CartReducer,
userLogin: userLoginReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdateProfileReducer,
shippingAddress: ShippingAddressReducer,
},
preloadedState: initialState, //for local storage
});
export default store;
Internaly createSlice reducers uses an immer.
Could you try to change your reducer to:
saveShippingAddress: (state, action) => {
console.log('hello');
console.log(action.payload);
// localStorage.setItem('shippingAdrress', JSON.stringify(action.payload));
state.shippingAddress = action.payload;
}
For more info I can suggest to visit https://redux-toolkit.js.org/usage/immer-reducers#immer-usage-patterns
Your code is working fine. I saw in redux devtools that shipping address is set to the values entered in the form after clicking submit. You should clarify more on what is it that is not working. If it's giving any kind of error, kindly share.
I have this two codes
import React, { useState, useEffect, createContext } from "react";
import { useNavigate } from "react-router-dom";
import { api, createSession } from "./Services/api"
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const navigate = useNavigate();
const [user, setUser] = useState();
const [loading, setLoading] = useState(true)
useEffect(() => {
const recoveredUser = localStorage.getItem("user")
if(recoveredUser){
setUser(JSON.parse(recoveredUser))
}
setLoading(false);
}, []);
const refreshPage = ()=>{
window.location.reload();
}
const login = async (username, pass) => {
const response = await createSession(username, pass);
const loggedUser = response.data.user;
const token = response.data.jwt;
localStorage.setItem("user", JSON.stringify(loggedUser))
localStorage.setItem("token", token)
api.defaults.headers.Authorization = {
"identifier": username,
"password": pass
}
setUser(loggedUser)
navigate("/");
};
const logout = () => {
localStorage.clear();
api.defaults.headers.Authorization = null;
setUser(null);
navigate("/login")
refreshPage();
};
return(
<AuthContext.Provider value={{authenticated: !!user, user, login, logout, loading}}> {children} </AuthContext.Provider>
)
}
and this
import axios from "axios";
export const api = axios.create({
baseURL: "https://communicationadmin.grupostra.com",
});
export const createSession = async(identifier, password) => {
return api.post('/auth/local', {identifier, password});
}
and this is my login screen
import Input from '../components/Input';
import styles from './Login.module.css';
import { useState, useContext } from 'react';
import { AuthContext } from '../Auth'
import logo from '../img/grupostra_horizontal.png'
import openEye from '../img/eye.png'
import closedEye from '../img/closedeye.png'
function Login(){
const { login } = useContext(AuthContext);
const [eyeImg, setEyeImg] = useState(openEye);
function handdleClick(){
if(eyeImg === openEye){
setEyeImg(closedEye);
document.querySelector('input:nth-child(4)').type = "";
} else {
setEyeImg(openEye)
document.querySelector('input:nth-child(4)').type = "password";
}
}
function handleSubmit(e){
e.preventDefault();
login(user, pass);
}
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
return(
<div className={styles.container}>
<div className={styles.form_container}>
<img src={logo} alt="group" srcset="" />
<p>Welcome</p>
<form onSubmit={handleSubmit}>
<label htmlFor="username">Usuário</label>
<Input type="text" name="username" id="username" placeholder="User" setProps={setUser} value={user}/>
<label htmlFor="password">Senha</label>
<Input type="password" name="password" id="password" placeholder="Pass" setProps={setPass} value={pass}/>
<span onClick={handdleClick}><img src={eyeImg} className={styles.eyeImg} /></span>
<Input className="btn-hover" type="submit" name="submit" id="submit" value="Enter" />
</form>
</div>
</div>
)
}
export default Login
My question is how can or where a write an alert to show to users when your credentials are wrong? I tried some things, but i don't have sucess . (I want to show in a login screen) ........................................................................................
If the server handle the "wrong credentials" as an HTTP Code 4XX or 5XX then you can just catch the exception an do something about it
async handleSubmit(e){
e.preventDefault();
await login(user, pass).catch(exp => alert("Something happened, maybe invalid credentials"))
}
can you check your api response when it is giving error .when we are creating login api's if any error is there we are adding exceptions handlers to show those messages .if that is the case use below code
*
import Input from '../components/Input';
import styles from './Login.module.css';
import { useState, useContext } from 'react';
import { AuthContext } from '../Auth'
import logo from '../img/grupostra_horizontal.png'
import openEye from '../img/eye.png'
import closedEye from '../img/closedeye.png'
function Login(){
const { login } = useContext(AuthContext);
const [eyeImg, setEyeImg] = useState(openEye);
const [error,seterror]=useState('')
function handdleClick(){
if(eyeImg === openEye){
setEyeImg(closedEye);
document.querySelector('input:nth-child(4)').type = "";
} else {
setEyeImg(openEye)
document.querySelector('input:nth-child(4)').type = "password";
}
}
async function handleSubmit(e){
try
{
e.preventDefault();
awiat login(user, pass);
} catch(error){
seterror(error.message.toString())
}
}
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
return(
<div className={styles.container}>
<div className={styles.form_container}>
<img src={logo} alt="group" srcset="" />
<p>Welcome</p>
<p>{error}</P>
<form onSubmit={handleSubmit}>
<label htmlFor="username">Usuário</label>
<Input type="text" name="username" id="username" placeholder="User" setProps={setUser} value={user}/>
<label htmlFor="password">Senha</label>
<Input type="password" name="password" id="password" placeholder="Pass" setProps={setPass} value={pass}/>
<span onClick={handdleClick}><img src={eyeImg} className={styles.eyeImg} /></span>
<Input className="btn-hover" type="submit" name="submit" id="submit" value="Enter" />
</form>
</div>
</div>
)
}
export default Login
I'm developing a CRUD and, in one of the components, when the user creates the post, a div is rendered with the title and content values of his post. I need his posts to be saved even when he navigates between pages (without refreshing the page). Currently I can create as many posts as I want and they will be lined up one below the other, but when I go back to the previous page they are deleted. I tried to implement redux in this part the way I implemented it to save the user input (creating a slice to save the posts) but it didn't work. I know normally posts wouldn't be deleted, so I'd like to know where I'm going wrong.
signup screen:
import React, {useState, useEffect} from "react";
import "../_assets/signup.css";
import "../_assets/App.css";
import { useDispatch } from 'react-redux';
import userSlice from '../redux/userslice';
import { useNavigate } from "react-router-dom";
function Signup() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [name, setName] = useState('')
const [buttonGrey, setButtonGrey] = useState('#cccccc')
useEffect(() => {
if (name!== '') {
setButtonGrey("black")
}
else {
setButtonGrey('#cccccc')
}
}, [name])
const handleSubmitForm= (e) => {
e.preventDefault()
dispatch(userSlice.actions.saveUser(name))
navigate("/main")
}
const handleChangeName = (text) => {
setName(text)
}
return (
<div className="container">
<div className="LoginBox">
<form onSubmit={handleSubmitForm}>
<h2>Welcome to codeleap network</h2>
<text>Please enter your username</text>
<input type="text" name="name" value={name} onChange = {e => handleChangeName(e.target.value)} placeholder="Jane Doe" />
<div className="button">
<button type="submit" style={{backgroundColor: buttonGrey}} disabled={!name} >
ENTER
</button>
</div>
</form>
</div>
</div>
);
}
export default Signup;
CRUD screen:
import React, { useState, useEffect } from "react";
import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { Navigate } from 'react-router-dom';
import postsSlice from '../redux/postsslice'
import Modal from "../components/modal.jsx";
function MainScreen() {
const dispatch = useDispatch();
const user = useSelector((state) => state.user)
const loadPosts = useSelector((state) => state.loadPosts)
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [newPosts, setNewPosts] = useState([])
const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");
useEffect(() => {
if (title && content !== "") {
setButtonGreyOut("black");
} else {
setButtonGreyOut("#cccccc");
}
},[title, content]);
const handleSubmitSendPost = (e) => {
e.preventDefault();
setNewPosts(newPosts.concat({title, content}))
dispatch(postsSlice.actions.savePosts(newPosts))
setTitle('')
setContent('')
};
const handleChangeTitle = (text) => {
setTitle(text);
};
const handleChangeContent = (text) => {
setContent(text);
};
const [openModal, setOpenModal] = useState();
if (user === '') {
return <Navigate to="/" />
} else {
return (
<div className="containerMainScreen">
{openModal && <Modal closeModal={setOpenModal} />}
<div className="bar">
<h1>Codeleap</h1>
</div>
<div className="boxPost">
<h2 style={{ fontWeight: 700 }}>What's on your mind?</h2>
<h2>Title</h2>
<form onSubmit={handleSubmitSendPost}>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={(e) => handleChangeTitle(e.target.value)}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={(e) => handleChangeContent(e.target.value)}
></textarea>
<button
className="createButton"
type="submit"
style={{ backgroundColor: buttonGreyOut }}
disabled={!title || !content}
>
CREATE
</button>
</form>
</div>
{newPosts.map((post) => (
<div className="boxPost">
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(true);
}}
/>
<FiEdit
style={{ color: "white", fontSize: "45px", paddingLeft: "23px" }}
/>
</div>
<div id="postowner">
<h3>#{user}</h3>
<h3>25 minutes ago</h3>
<br></br>
<textarea style={{ border: "none" }}>{post.content}</textarea>
</div>
</div>
))}
</div>
);
}
}export default MainScreen;
store.js:
import { configureStore } from '#reduxjs/toolkit';
import userSlice from './userslice';
import postsSlice from './postsslice'
export const store = configureStore({
reducer: {
user: userSlice.reducer,
loadPosts: postsSlice.reducer
},
})
postsSlice:
import { createSlice } from "#reduxjs/toolkit";
const postsSlice = createSlice({
name: "posts",
initialState: "",
reducers: {
savePosts: (state, action) => action.payload
}
});
export default postsSlice
CRUD screenshot:
https://i.stack.imgur.com/YoCJz.png
You are dispatching the savePosts action with the value of newPosts from before the current posts are added to it. The problem is in these lines:
setNewPosts(newPosts.concat({title, content}))
dispatch(postsSlice.actions.savePosts(newPosts))
Try something like this instead:
const posts = newPosts.concat({title, content})
setNewPosts(posts)
dispatch(postsSlice.actions.savePosts(posts))
It does not make sense to store the posts array in Redux and also store it in local component state. In my opinion you should ditch the component newPosts state and access the posts via Redux with useSelector.
I would also recommend dispatching an addPost action which requires just the current post. Let the reducer handle adding it to the array.
The state is an array of posts, so your initialState should be an empty array rather than an empty string.
const postsSlice = createSlice({
name: "posts",
initialState: [],
reducers: {
// add one post to the array.
addPost: (state, action) => {
state.push(action.payload); // modifies the draft state.
},
// replace the entire array.
replacePosts: (state, action) => action.payload
}
});
function MainScreen() {
const dispatch = useDispatch();
const user = useSelector((state) => state.user)
const posts = useSelector((state) => state.loadPosts)
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
/* ... */
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(postsSlice.actions.addPost({title, content}))
setTitle('')
setContent('')
};
Currently, when a user signs up they are being created as a user in the firebase authentication. I am trying to add that newly created user directly into a firestore collection upon creation.
The following is the AuthContext.js
import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase";
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup (email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
function logout() {
return auth.signOut();
}
function resetPassword(email) {
return auth.sendPasswordResetEmail(email);
}
function updateEmail(email) {
return currentUser.updateEmail(email);
}
function updatePassword(password) {
return currentUser.updatePassword(password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
The following is Signup.js
import React, { useRef, useState } from 'react'
import { Form, Button, Card, Alert } from "react-bootstrap"
import { Link, useHistory } from "react-router-dom"
import { useAuth } from './contexts/AuthContext'
export default function Signup() {
const emailRef = useRef()
const passwordRef = useRef()
const passwordConfirmRef = useRef()
const { signup } = useAuth()
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
const history = useHistory()
async function handleSubmit(e){
e.preventDefault()
if (passwordRef.current.value !==
passwordConfirmRef.current.value) {
return setError('Passwords do not match')
}
try{
setError('')
setLoading(true)
await signup(emailRef.current.value,passwordRef.current.value)
history.push("/")
} catch {
setError('Failed to create an account')
}
setLoading(false)
}
return (
<>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Sign Up</h2>
{error && <Alert variant="danger">{error}</Alert>}
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control type="email" ref={emailRef} required />
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control type="password" ref={passwordRef} required />
</Form.Group>
<Form.Group id="password-confirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control type="password" ref={passwordConfirmRef} required />
</Form.Group>
<Button disabled={loading} className="w-100" type="submit">
Sign Up
</Button>
</Form>
<div className="w-100 text-center mt-2">
Already have an account? <Link to="/login">Log In</Link>
</div>
</Card.Body>
</Card>
</>
)
}
Thank you in advance, any help will be greatly appreciated.
The creation of the user in Firebase Auth will happen no matter what you do, but you can take advantage of that by creating a Cloud Function that triggers everytime a user is created in Firebase auth and then create a new document in Firestore representing that user. Here is a Cloud Function example that does just that:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.createUser = functions.auth.user().onCreate((user) => {
const { uid } = user;
const userCollection = db.collection('users');
userCollection.doc(uid).set({
someData: "123"
});
});
Also in the documentation you can see how to deploy Cloud Functions, in case you are not familiar with it.
I submit a request to the server and then want to get the result without reloading the page (SPA principle), how can this be done using useEffect()?
I tried to do something like this:
useEffect (() => {
addProduct ();
})
but it's was a bad idea
import React, {useState, useEffect} from 'react';
import api from './api';
const HandleProduct = () => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = () =>{
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
console.log(res);
})
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
When the callback with response is called you've got the repsonse with all data sent from API. Let's assume you want to get ID. I will add new hook for storing ID, setting it after POST method is completed, and displaying it.
const [productId, setProductId] = useState(null);
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
onSubmit() {
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
setProudctId(JSON.parse(res).id);
})
}
return (
<div>
{productId && <span>Your productId: {productId} </span>}
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
Your code seems legit, yet, given that is not working, I'll give you another option to do it.
In App.js
<Router >
<ProductsProvider>
<Route exact path="/products" component={ProductsList} props={...props} />
<Route exact path={'/products/add'} component={HandleProduct}
props={...props} />
</ProductsProvider>
</Router>
In HandleProduct.js
import React, {useState} from 'react';
import api from './api';
import { Redirect } from 'react-router'
const HandleProduct = ({history}) => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = (e) =>{
e.preventDefault();
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
history.push('/products');
})
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
import React, {useContext} from 'react';
import {ProductsContext} from './ProductsContext';
const ProductsList = () => {
const [data] = useContext(ProductsContext);
return (
<div>
{console.log(data)}
{data.products.map((product, index)=>(
<div key={index}>
<p>{product.name}</p>
<p><i>{product.description}</i></p>
</div>
))}
</div>
);
}
export default ProductsList;