Here i have my component code for SignIng Up user and check for Error. At first error is null.
let error = useSelector((state) => state.authReducer.error);
const checkErrorLoading = () => {
console.log("If error found"); //At first it gives null, but on backend there is error
toast.error(error);
console.log(loading, error);
};
const handleSubmit = async (e) => {
if (isSignup) {
dispatch(signup(form, history));
checkErrorLoading();
} else {
dispatch(signin(form, history));
checkErrorLoading();
}
};
Now at my singupForm, i provide wrong input or wrong data. The backend gives me error that is completely fine.
ISSUE => But when i click on Login button. At first attempt it does not provide any error message. After second attempt it works fine, but not at first attempt. At first attempt it gives me Error value NULL while there is still an error
Here is my action.
export const signup = (formData, history) => async (dispatch) => {
try {
const res = await api.signUp(formData);
dispatch({ type: authConstants.AUTH_REQUEST });
if (res.status === 200) {
const { data } = res;
console.log(data);
dispatch({
type: authConstants.AUTH_SUCCESS,
payload: data,
});
}
console.log(res.status);
history.push("/");
} catch (error) {
console.log(error.response);
dispatch({
type: authConstants.AUTH_FAILURE,
payload: error.response.data.error,
});
}
};
and than reducer.
const initialState = {
authData: null,
error: null,
loading: false,
};
const authReducer = (state = initialState, action) => {
switch (action.type) {
case authConstants.AUTH_REQUEST:
return { ...state, loading: true, error: null };
case authConstants.AUTH_SUCCESS:
localStorage.setItem("profile", JSON.stringify({ ...action?.payload }));
return { ...state, authData: action?.data, loading: false, error: null };
case authConstants.AUTH_FAILURE:
console.log(action.payload);
return { ...state, loading: false, error: action.payload };
}
You should use useEffect instead of local function (checkErrorLoading ) for such cases:
useEffect(() => {
console.log("If error found");
toast.error(error);
console.log(loading, error);
},[error]);
Currently what you doing is creating local function that closures error variable, which initially is null + state is updated asynchronously, so you cannot execute function right after dispatching (even if variable wouldn't be closured, you will not have fresh state there)
Related
This is the async thunk function in my slice, I'm using axios (http is coming from another file defined as axios.create())
export const loginUser = createAsyncThunk("auth/fetchUsers", async (data) => {
const response = await http.post("/login", data);
return response.data;
});
In above data is an object passed through dispatch method (data contains email and password).
This is my initial state,
const initialState = {
loading: false,
user: [],
error: [],
},
This is my reducer, I use extra reducers So I can stage the above loginUser into stages as pending,fullfilled,rejected
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
},
extraReducers: (builder) => {
builder.addCase(loginUser.pending, (state) => {
state.apiHandler.loading = true;
});
builder.addCase(loginUser.fulfilled, (state, action) => {
state.apiHandler.loading = false;
state.apiHandler.user = action.payload;
state.apiHandler.error = [];
});
builder.addCase(loginUser.rejected, (state, action) => {
state.apiHandler.loading = false;
state.apiHandler.user = [];
state.apiHandler.error = action.error.message;
});
},
});
I'm trying to get the exact error messages returning from Laravel. But I'm getting the message as Request failed with status code 422 in the rejected cycle.With postman I'm retrieving the exact error,
{
"message": "Incorrect Credentials"
}
This is a potion of my Laravel code, (So if something is not fullfilled it should return errors),
$request->validate([
'email' => 'required | exists:users',
'password' => 'required'
]);
My question is: How to retrieve the exact error messages with asyncThunk reject method without retrieving Request failed with status code 422 as the error.
I don't know about your project structure. However, you can actually get the Laravel errors, by waiting for a json response and accessing response's .errors .
For Example:
if (response. Status === 422) {
const validation = await response.json();
this.message= validation.errors
this.error= true;
}
According to your project structure, place this logic in your responses, anywhere suitable.
What I see here is you return response.data anyways, while laravel errors reside in response.errors object.
I hope it helps.
I attached try catch and passed the 2nd argument as {rejectWithValue} for the createAsyncThunk, and catching the errors with action.payload in case.rejected ,
export const loginUser = createAsyncThunk(
"auth/fetchUsers",
async (payload, { rejectWithValue }) => {
try {
const { data } = await http.post("/login", payload);
return data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
extraReducers: (builder) => {
...
builder.addCase(loginUser.rejected, (state, action) => {
state.loading = false;
state.user = [];
state.error = action.payload;
});
},
Using react.js & firebase
The code below represents a simple button which increases/decreases +1/-1 whenever its clicked. It also updates one of the documents on the backend (using firebase). Everything seems to work fine on the surface but not on firebase. When you click on the button, it'll show +1 on the UI and console.log but not on firebase. In other words when plusCount state is at 0, it shows +1 on firebase and when plusCount state is at +1, it shows 0 on firebase. How can I fix this to make sure it shows the same number on the frontend and the backend? I also added the useFirestore hook component below, there may be a mistake that I'm unaware of in there somewhere.
Thank you for any help.
Button component:
import React, { useState } from 'react';
import { useFirestore } from "../../hooks/useFirestore"
export default function Testing({ doc }) {
const { updateDocument } = useFirestore('projects')
const [plusActive, setPlusActive] = useState(false)
const [plusCount, setPlusCount] = useState(0)
function p() {
setPlusActive(prevState => !prevState);
plusActive ? setPlusCount(plusCount - 1) : setPlusCount(plusCount + 1)
}
const handlePlus = (e) => {
e.preventDefault();
p();
updateDocument(doc.id, {
votes: plusCount
})
}
console.log(plusCount)
return (
<div>
<button onClick={handlePlus}>like | {plusCount}</button>
</div>
)
}
useFirestore hook component:
import { projectFirestore, timestamp } from "../firebase/config"
let initialState = {
document: null,
isPending: false,
error: null,
success: null,
}
const firestoreReducer = (state, action) => {
switch (action.type) {
case 'IS_PENDING':
return { isPending: true, document: null, success: false, error: null }
case 'ADDED_DOCUMENT':
return { isPending: false, document: action.payload, success: true, error: null }
case 'DELETED_DOCUMENT':
return { isPending: false, document: null, success: true, error: null }
case 'ERROR':
return { isPending: false, document: null, success: false, error: action.payload }
case "UPDATED_DOCUMENT":
return { isPending: false, document: action.payload, success: true, error: null }
default:
return state
}
}
export const useFirestore = (collection) => {
const [response, dispatch] = useReducer(firestoreReducer, initialState)
const [isCancelled, setIsCancelled] = useState(false)
// collection ref
const ref = projectFirestore.collection(collection)
// only dispatch if not cancelled
const dispatchIfNotCancelled = (action) => {
if (!isCancelled) {
dispatch(action)
}
}
// add a document
const addDocument = async (doc) => {
dispatch({ type: 'IS_PENDING' })
try {
const createdAt = timestamp.fromDate(new Date())
const addedDocument = await ref.add({ ...doc, createdAt })
dispatchIfNotCancelled({ type: 'ADDED_DOCUMENT', payload: addedDocument })
}
catch (err) {
dispatchIfNotCancelled({ type: 'ERROR', payload: err.message })
}
}
// delete a document
const deleteDocument = async (id) => {
dispatch({ type: 'IS_PENDING' })
try {
await ref.doc(id).delete()
dispatchIfNotCancelled({ type: 'DELETED_DOCUMENT' })
}
catch (err) {
dispatchIfNotCancelled({ type: 'ERROR', payload: 'could not delete' })
}
}
// update a document
const updateDocument = async (id, updates) => {
dispatch({ type: "IS_PENDING" })
try {
const updatedDocument = await ref.doc(id).update(updates)
dispatchIfNotCancelled({ type: "UPDATED_DOCUMENT", payload: updatedDocument })
return updatedDocument
}
catch (error) {
dispatchIfNotCancelled({ type: "ERROR", payload: error })
return null
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return { addDocument, deleteDocument, updateDocument, response }
}```
For your use-case, you should useEffect() to listen the changes for plusCount. See code below:
useEffect(() => {
updateDocument('test', {
votes: plusCount
})
}, [plusCount]);
const handlePlus = (e) => {
e.preventDefault();
setPlusActive(prevState => !prevState);
plusActive ? setPlusCount(plusCount - 1) : setPlusCount(plusCount + 1)
}
Everytime you click the button it will listen to the changes of plusCount which then the updateDocument will also be triggered together with the updated state. See below screenshot for the result:
As you can see, the frontend and backend is now aligned.
You can find more information by checking out this documentation.
Bit of a weird one. In my component I am getting a "task" object from my "taskDetails" reducer. Then I have a useEffect function that checks if the task.title is not set, then to call the action to get the task.
However when the page loads I get an error cannot read property 'title' of null which is strange because when I look in my Redux dev tools, the task is there, all its data inside it, yet it can't be retrieved.
Here is the relevant code:
const taskId = useParams<{id: string}>().id;
const dispatch = useDispatch();
const history = useHistory();
const [deleting, setDeleting] = useState(false)
const taskDetails = useSelector((state: RootStateOrAny) => state.taskDetails);
const { loading, error, task } = taskDetails;
const successDelete = true;
const deleteTaskHandler = () => {
}
useEffect(() => {
if(!successDelete) {
history.push('/home/admin/tasks')
} else {
if(!task.title || task._id !== taskId) {
dispatch(getTaskDetails(taskId))
}
}
},[dispatch, task, taskId, history, successDelete])
REDUCER
export const taskDetailsReducer = (state = { task: {} }, action) => {
switch(action.type) {
case TASK_DETAILS_REQUEST:
return { loading: true }
case TASK_DETAILS_SUCCESS:
return { loading: false, success: true, task: action.payload }
case TASK_DETAILS_FAIL:
return { loading: false, error: action.payload }
case TASK_DETAILS_RESET:
return { task: {} }
default:
return state
}
}
ACTION
export const getTaskDetails = id => async (dispatch) => {
try {
dispatch({
type: TASK_DETAILS_REQUEST
})
const { data } = await axios.get(`http://localhost:5000/api/tasks/${id}`)
dispatch({
type: TASK_DETAILS_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: TASK_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message
})
}
}
In my reducer in the TASK_DETAILS_REQUEST case, I just had loading: false.
I had failed to specify the original content of the state, I did this by adding ...state.
export const taskDetailsReducer = (state = { task: {} }, action) => {
switch(action.type) {
case TASK_DETAILS_REQUEST:
return { ...state, loading: true }
case TASK_DETAILS_SUCCESS:
return { loading: false, success: true, task: action.payload }
case TASK_DETAILS_FAIL:
return { loading: false, error: action.payload }
case TASK_DETAILS_RESET:
return { task: {} }
default:
return state
}
}
I need help with custom hooks. I created a custom hook to make api calls (useApiCall), I am calling it from my authActions.js file to send username and password to the server to signing up a new user. I am getting an error saying I am miss using hooks in the useApiCall functional component.
When I remove the logic for useReducer in the useApiCall functional component, it says I am misusing useState which comes after useReducer. It will not let me use hooks in the React functional component useApiCall.
I know I am breaking the rule of hooks, but what rule is that? I compared my code with other custom hooks and there is no difference.
In the useApiCall function I striped away all of my logic and used a single useState and got the same error. What am I doing wrong?
Hook called from (authActions): https://github.com/SMasood1/Chat-App/blob/main/client/src/context/authContext/authAction.js
Custom Hook (useApiCall): https://github.com/SMasood1/Chat-App/blob/main/client/src/context/a
import { useState, useEffect, useReducer } from 'react';
// Can make this more elegant and able to handle different types of methods and headers
const FETCH_INIT = 'FETCH_INIT';
const FETCH_SUCCESS = 'FETCH_SUCCESS';
const FETCH_FAILURE = 'FETCH_FAILURE';
const dataReducer = (state, action) => {
switch (action.type) {
case FETCH_INIT:
return {
...state,
isLoading: true,
isError: false
}
case FETCH_SUCCESS:
return {
...state,
isLoading: false,
isError: false,
data: action.payload
}
case FETCH_FAILURE:
return {
...state,
isLoading: false,
isError: true,
data: action.error
}
default:
throw new Error();
}
}
export const useApiCall = (initialUrl, initialMethod, initialData) => {
const [state, dispatch] = useReducer(dataReducer, {
isLoading: false,
isError: null,
data: initialData ? initialData : ''
})
const [method, setMethod] = useState(initialMethod ? initialMethod : null);
const [url, setUrl] = useState(initialUrl ? initialUrl : '');
useEffect(() => {
const fetchData = async () => {
await dispatch({ type: FETCH_INIT });
let response;
try {
switch (method) {
case 'GET':
response = await fetch(url);
break;
case 'POST':
response = await fetch(url, {
method: method,
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(state.data)
});
break;
default:
console.log('Incorrect HTTP Request Method');
}
if (response.ok) {
let resBody = await response.json();
dispatch({ type: FETCH_SUCCESS, payload: resBody });
} else {
let resBody = await response.json();
dispatch({ type: FETCH_FAILURE, error: resBody });
}
} catch (error) {
dispatch({ type: FETCH_FAILURE, error: 'Unable to send request!' });
}
}
if (method && url) {
fetchData();
}
}, [url, method, state.data]);
return [state, setUrl, setMethod]
}
Error I received is below:
Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen
for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
The function below fetches a list of posts asynchronously and sends the received data to my app's Redux store.
The function handles both the fetching of the initial set of posts and that of subsequent posts that the user can trigger by clicking on a 'Load more' button.
export const fetchFilteredPosts = (filter, reset) => async(dispatch, getState, api) => {
if (reset) {
dispatch({
type: 'RESET_FILTERED_POSTS'
});
}
dispatch({
type: 'IS_FETCHING_FILTERED_POSTS'
});
try {
const currentPage = getState().filteredPosts.currentPage;
const nextPage = currentPage == 0 ? 1 : (currentPage + 1);
const filteredPosts = await api.get('/wp-json/wp/v2/posts?tag=' + filter + '&page=' + nextPage);
dispatch({
type: 'HAS_FETCHED_FILTERED_POSTS',
payload: {
data: filteredPosts.data,
currentPage: nextPage
}
});
} catch (error) {
dispatch({
type: 'FAILED_FETCHING_FILTERED_POSTS',
payload: error
});
}
}
Here's my Redux store:
import { filteredPostsPerPage } from '../config';
const initState = {
canFetchMore: false,
currentPage: 0,
data: null,
fetchingError: null,
isFetching: null,
perPage: filteredPostsPerPage
}
export default (state = initState, action) => {
switch (action.type) {
case 'IS_FETCHING_FILTERED_POSTS':
return {
...state,
isFetching: true,
fetchingError: false
}
case 'HAS_FETCHED_FILTERED_POSTS':
const posts = action.payload.data;
return {
...state,
data: state.data === null ? posts : state.data.concat(posts),
isFetching: false,
canFetchMore: posts.length >= state.perPage,
currentPage: action.payload.currentPage
}
case 'FAILED_FETCHING_FILTERED_POSTS':
return {
...state,
isFetching: false,
fetchingError: action.payload
}
case 'RESET_FILTERED_POSTS':
return initState;
default:
return state;
}
}
Suppose I have set 10 as the number of posts to display per page, and that the user has selected a category in which there are exactly 10 posts. If they're going to click on the Load More button, the app will throw this error:
{
"code": "rest_post_invalid_page_number",
"message": "The page number requested is larger than the number of pages available.",
"data": {
"status": 400
}
}
How can I listen for this exact error in the catch part of my function, so that I can display a message to the user, something like No more posts in this category? I guess I need to access the API request's response, but I'm not sure how to do that in this case.
You cant listen to a specific error, you have to listen for all.
You could use an if-statement:
try {
/* ... */
} catch (e) {
if (e.data.status === 400) {
/* handle your error */
} else {
}
}
Found it. This has something to do with using the Axios library, which I didn't mention I was using 'cause I didn't know that with Axios you need to work with error.response, not simply error. So if you use Axios you can catch the error as follows:
try {
/* ... */
} catch (error) {
if (error.response.data.status === 400) {
/* handle your error */
} else {
}
}