How can I handle multiple conditional "rejectWithValue" in redux_toolkit - javascript

export const login = createAsyncThunk(
"auth/login",
async (inputs:any, thunkAPI) => {
try {
const data = await axios.post("/api/auth/login", inputs);
return { user: data };
} catch (err:any) {
if(err.response){return thunkAPI.rejectWithValue(err.response.data)}
if (err.request) {return thunkAPI.rejectWithValue("Can't Not Access The Server")}
}
}
);
Above it return an error, I would like to know it's there have any solution

Related

Why can't I access data after fetching?

I'm trying to keep session stayed logged in after refreshing the browser. The user data that is being fetched is not rendering after being fetched. The console is saying "Cannot read properties of undefined (reading 'user'). This is my code for the login/sign up page.
The data I'm trying to access is in the picture below:
(Auth.js)
const Auth = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [isSignup, setIsSignup] = useState(false);
const [inputs, setInputs] = useState({
name: "",
username: "",
email: "",
password: ""
})
const handleChange = (e) => {
setInputs(prevState => {
return {
...prevState,
[e.target.name]: e.target.value
}
})
}
const sendRequest = async (type = '') => {
const res = await axios.post(`/user/${type}`, {
name: inputs.name,
email: inputs.email,
username: inputs.username,
password: inputs.password,
}).catch(error => console.log(error))
const data = await res.data;
console.log(data)
return data;
}
const handleSubmit = (e) => {
e.preventDefault()
console.log(inputs)
if (isSignup) {
sendRequest("signup")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
} else {
sendRequest("login")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
}
}
Redux store file
const authSlice = createSlice({
name: "auth",
initialState: { isLoggedIn: false },
reducers: {
login(state) {
state.isLoggedIn = true
},
logout(state) {
state.isLoggedIn = false
}
}
})
export const authActions = authSlice.actions
export const store = configureStore({
reducer: authSlice.reducer
})
Chaining promises using .then() passes the resolved value from one to the next. With this code...
sendRequest("...")
.then(() => dispatch(authActions.login()))
.then(() => navigate("/posts"))
.then(data => localStorage.setItem('token', data.user))
You're passing the returned / resolved value from navigate("/posts") to the next .then() callback. The navigate() function returns void therefore data will be undefined.
Also, your redux action doesn't return the user so you can't chain from that either.
To access the user data, you need to return it from sendRequest()...
const sendRequest = async (type = "") => {
try {
const { data } = await axios.post(`/user/${type}`, { ...inputs });
console.log("sendRequest", type, data);
return data;
} catch (err) {
console.error("sendRequest", type, err.toJSON());
throw new Error(`sendRequest(${type}) failed`);
}
};
After that, all you really need is this...
sendRequest("...")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
Since you're using redux, I would highly recommend moving the localStorage part out of your component and into your store as a side-effect.

How To Do JavaScript Object Oriented Programming client side The Right Way?

I'm doing an API using React, SQL and Sequelize. I'm already finished but now I was asked to do my API request using JavaScript oriented object. The problem is, I don't really know how to do it, and I don't really understand my errors.
This is the class I am trying to do:
API class
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
return await fetch('api/post/')
.then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res;
})
.then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
}
export const apiRequest = new Api();
Axios config
import axios from 'axios';
class AxiosConfig {
constructor() {
this.axios = axios.create();
this.axios.defaults.baseURL = `${process.env.REACT_APP_API_URL}`;
this.axios.defaults.headers = {
'Content-Type': 'application/json',
};
//All request will wait 2 seconds before timeout
this.axios.defaults.timeout = 2000;
this.axios.defaults.withCredentials = true;
}
GET = async (url) => {
return await this.axios.get(`/${url}`);
}
POST = async (url, payload) => {
return await this.axios.post(`/${url}`, payload);
}
PUT = async (url, payload) => {
return await this.axios.put(`/${url}`, payload);
}
DELETE = async (url) => {
return await this.axios.delete(`/${url}`);
}
}
export const axiosInstance = new AxiosConfig();
HandlePost() is the function I'm tring to put in oriented object
NewPostForm
import React, { useContext, useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import { UserContext } from '../../UserContext';
import { apiRequest } from '../../utils/api';
import { axiosInstance } from '../../utils/AxiosConfig'
import landscape from './../../assets/icons/landscape.svg'
const NewPostForm = () => {
const uid = useContext(UserContext)
const [userPicture, setUserPicture] = useState('')
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [message, setMessage] = useState('')
const [postPicture, setPostPicture] = useState('')
const [file, setFile] = useState('')
useEffect(() => {
const getUserInfo = async () => {
if (uid !== null) {
const userId = uid.userId
await axiosInstance.GET (`api/auth/${userId}`)
.then((res) => {
setFirstName(res.data.firstName)
setLastName(res.data.lastName)
setUserPicture(res.data.profile)
})
.catch((err) => {
console.log(err)
})
}
}
getUserInfo()
}, [uid, firstName, lastName])
console.log('parfait', axiosInstance.GET(`api/post`)) // old one, works perfectly
console.log('objectif', apiRequest.getPost()) // new one, not getting the object I need
// console.log(axiosInstance.GET(apiRequest.getPost`${uid.userId}`)) // error
const handlePost = async () => {
if (message || postPicture) {
const data = new FormData();
data.append("UserId", uid.userId);
data.append("content", message);
if (file) {
data.append("image", file);
}
try {
const res = await axiosInstance.POST(`api/post`, data);
console.log('File uploaded', res.data);
// window.location = '/'
} catch (err) {
console.error('Failed to upload file', err);
}
cancelPost()
} else {
alert("Veuillez entrer un message")
}
}
const handlePicture = (e) => {
setPostPicture(URL.createObjectURL(e.target.files[0]))
setFile(e.target.files[0])
}
const cancelPost = () => {
setMessage('')
setPostPicture('')
setFile('')
}
return (
<form className='post-container' >
<h2 className='h1'>Créer un post</h2>
<NavLink to="/profile">
<figure title='Profil utilisateur' className='new card-header'>
<img className='nav-profile' src={userPicture ? userPicture : "./images/img/profile.png"} width='50px' alt="profil de l'utilisateur" />
<h3 className='h2'>{firstName} {lastName}</h3>
</figure>
</NavLink>
<div className='post-form'>
<textarea
type="text"
name="message"
id="message"
cols="50"
rows="5"
placeholder="Quoi de neuf ?"
onChange={(e) => setMessage(e.target.value)}
value={message}
></textarea>
{postPicture && <img src={postPicture} alt="preview" className="img-preview" />}
</div>
<div className='footer-form'>
<div className='icon'>
<input
type="file"
id='file-upload'
name='file'
accept='.jpg, .jpeg, .png'
onChange={(e) => handlePicture(e)}
/>
<label className='file-input__label' htmlFor="file-upload">
<img className='svg' src={landscape} alt="upload icone paysage" />
Ajouter une l'image
</label>
</div>
<div className='new button-container'>
{message || postPicture ? (
<button className='new cancel-btn' onClick={(e) => cancelPost()}>Annuler</button>
) : null}
<button className='new validate-btn' onClick={(e) => handlePost()}>Envoyer</button>
</div>
</div>
</form>
);
};
export default NewPostForm;
My first console.log() returning the object that I need, when the second (supposed oriented object one) doesn't. I don't know what I'm doing wrong and the documentation doesn't help, I don't have the "good" questions.
Thanks
EDIT :
class Api {
constructor(hostName, token) {
this.hostName = hostName
this.token = token
}
async getPost() {
try {
const res = await fetch('api/post')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}
}
export const apiRequest = new Api();
The problem is with the code that how you used fetch method. This is from the documentation.
The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.
You have to update your getPost method like below.
async getPost() {
return await fetch('api/post/').then(res => {
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return res.json();
}).then(post => {
console.log(post)
return post
})
.catch(err => {
console.log("Page non trouvée")
console.log(err)
})
}
Check the documentation
You already using async and await so you don't need to use .then. The code can be updated like below.
async getPost() {
try {
const res = await fetch('api/post/')
if (!res.ok) {
throw Error(res.statusText + "-" + res.url);
}
return await res.json();
} catch(err) {
console.log("Page non trouvée")
console.log(err)
}
}

How do I send a variable from Express.js backend to React.js frontend?

I am trying to send my variable 'backEndResponse' with its value from my Express.js backend to my React.js Frontend. I am not quite sure how to send a variable from the backend to the frontend. I have searched around and can't find any good resources. I would appreciate any help.
Express.js Backend
function getcookie(req) {
var authCookie = req.headers.cookie;
if (authCookie = req.headers.cookie) {
try {
return authCookie
.split('; ')
.find(row => row.startsWith('Auth='))
.split('=')[1];
} finally {
if (authCookie = result) {
backEndResponse = true
console.log(backEndResponse);
console.log(result);
} else {
backEndResponse = false
console.log(backEndResponse);
console.log(result);
}
}
} else {
}
}
app.get('/auth', (req, res) => {
getcookie(req)
if (backEndResponse) {
res.json(backEndResponse); // OR json({ message: "Authorised" })
} else {
res.json(backEndResponse); // OR json({ message: "Unauthorised" })
}
});
Frontend React.js
const useAuth = () => {
const [data, setData] = useState();
useEffect(() => {
const fetchAuthData = () => {
const result = axios('http://localhost:5000/auth');
console.log(result)
setData(result.data);
};
fetchAuthData()
}, []);
// Logic to check if backEndResponse is true or false
if (data) {
const authorized = {loggedIn: true}
return authorized && authorized.loggedIn;
} else {
const authorized = {loggedIn: false}
return authorized && authorized.loggedIn;
}
}
const ProtectedRoutes = () => {
const isAuth = useAuth();
return isAuth ? <Outlet/> : <Navigate to="/login" />;
}
You won't be able to send a variable directly, rather you will send a payload in a certain shape that best represents the data suited to the applications needs. To send a response payload in an express route use something like the following:
app.get('/auth', (req, res) => {
// do some logic for `backEndResponse`...
res.json(backEndResponse);
});
If you were intending to provide more information in the response such as HTTP headers differing based on the of backEndResponse then you might consider:
app.get('/auth', (req, res) => {
// do some logic for `backEndResponse`...
// send HTTP Ok if true, otherwise Bad Request
// consider handling 400 and/or 500 errors too
if (backEndResponse) {
res.status(200).json(true); // OR json({ message: "Authorised" })
} else {
res.status(401).json(false); // OR json({ message: "Unauthorised" })
}
});
A component fetching the above endpoint would be similar to:
const MyComponent = () => {
const [data, setData] = useState();
useEffect(() => {
const fetchAuthData = async () => {
const result = await axios('http://localhost:5000/auth');
setData(result.data); // true/false OR { message: "Authorised" }
};
fetchAuthData();
}, []);
// display payload
return (<div>{JSON.stringify(data)}</div>)
}
There is an opportunity to refactor the above into a custom hook should you find the need to reuse the functionality across multiple components.
axios request is async function, so you should do like that,
const useAuth = async () => {
try {
const res = await axios.get('http://localhost:5000/auth', {
withCredentials: true
})
return true
} catch (e) {
return false
}
};

Create dynamic routes by id from Next JS pages api

I have a page with a list of objects called stories that displays all my stories in an array. I also have a detail page with displays an individual story.
I want to click on a link on any given story on the list, then it will navigate me to the individual story. I want to use _id as my dynamic part of the URL, as shown in the GraphQL below.
My Graphql
export const listAllStories = () => {
const query = gql`
query StoryEntries($size: Int) {
storyEntries(_size: $size) {
data {
_id
_ts
name
premises{
data{
_id
content
}
}
createdAt
}
}
}
`
return graphQLClient
.request(query, { size: 999 })
.then(({ storyEntries: { data } }) => data)
}
IN MY PAGES API I HAVE
export default async function handler(req, res) {
const handlers = {
GET: async () => {
const storyEntries = await listAllStories()
res.json(storyEntries)
},
}
if (!handlers[req.method]) {
return res.status(405).end()
}
await handlers[req.method]()
}
ON THE STORY LIST PAGE I HAVE
const ENTRIES_PATH = '/api/entries/allStories'
const useEntriesFlow = ({ initialEntries }) => {
const { data: entries } = useSWR(ENTRIES_PATH, {
initialData: initialEntries,
})
const EntryItem = ({ entry }) => (
<>
{entries?.map((entry) => (
{entry.name}
<Link href="/story/[storyId]" as={`/story/${entry._id}`}>
<a>Go</a>
</Link>
))}
</>
)
export const getStaticProps = async () => ({
props: {
initialEntries: await listAllStories(),
},
revalidate: 1,
})
This is fine and works.
**AND THEN ON THE DETAIL PAGE FOR EACH INDIVIDUAL STORY [storyId].js I HAVE **
export default function Story({story}) {
const router = useRouter()
const storyId = router.query.storyId
return(
<>
<h5>hello {story._id}</h5>
</>
)
}
export const getStaticPaths = async () => {
const res = await fetch(`${server}/api/entries/allStories/`);
const { data } = await res.json();
const paths = data.map(story => {
return {
params: { id: story._id.toString() }
}
// trying to get the _id from each story
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const { storyId } = context.query; // Your dynamic page is [storyId].js
const server = "http://localhost:3000";
const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
console.log(res)
const { data } = await res.json();
return {
props: { story: data }
}
}
ERROR
TypeError: Cannot read properties of undefined (reading 'map')
QUESTION
All I want to do is click on any story link, then it takes me to the details page, via the _id. I have tried a few things but I'm doing something (or some things) wrong.
Any help will be greatly appreciated.
EDIT AFTER. ERROR I'M GETTING. I'm not able to map my results on getStaticPaths
export const getStaticProps = async (context) => {
const { storyId } = context.query; // Your dynamic page is [storyId].js
const server = "YOUR SERVER VARIABLE";
const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
const { data } = await res.json();
return {
props: { story: data }
}
}
uncomment
const router = useRouter()
const storyId = router.query.storyId
// some helpful links
// https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required
// https://stackoverflow.com/questions/65783199/error-getstaticpaths-is-required-for-dynamic-ssg-pages-and-is-missing-for-xxx
export const getStaticPaths = async () => {
const server = "http://localhost:3000";
const data = await fetch(`${server}/api/entries/allStories/`).then(res => res.json() )
const paths = data.map(({_id}) => ({
params: { storyId: _id },
}))
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const storyId = context.params.storyId; // Your dynamic page is [storyId].js
const server = "http://localhost:3000";
// const res = await fetch(`${server}/api/entries/allStories/${storyId}`);
// trying to get the params._id from each story
// single api call (here)
const res = await fetch(`${server}/api/entries/allStories/`);
// removing const { data } because the data will be returned when calling res.json()
const data = await res.json();
// instead of the calling the single api (just a fix not recommended to access [0] directly )
return {
props: { story: data.filter(story => story._id === storyId)[0] }
}
}

how to get firebase use in nextJS getserversideprops

I want to get 'sample' document in Firestore using getServerSideProps if there is signed user.
Code below doesn't work. It's result is 'can't read'
What should I do? or is there the other way?
export const getServerSideProps = () => {
let currentUser = []
authService.onAuthStateChanged(async user => {
if(user) {
const docRef = dbService.collection('whole_users').doc('sample').get()
await docRef.then((doc) => {
if(doc.exists) {
currentUser.push(doc.data())
}
})
} else {
console.log("can't read")
}
})
return {
props: {currentUser}
}
}
The first:
You call get() without an await. Chaneg your code to this:
export const getServerSideProps = () => {
let currentUser = []
authService.onAuthStateChanged(async user => {
if(user) {
const docRef = dbService.collection('whole_users').doc('sample')
await docRef.get().then((doc) => {
if(doc.exists) {
currentUser.push(doc.data())
}
})
} else {
console.log("can't read")
}
})
return {
props: {currentUser}
}
}
The second: onAuthStateChanged is only for the client side. To access the auth state on the server side you would need to put the auth state into a provider. Here is an example how to do it.

Categories