I explain my problem:
I have two functions, one to create a garden and another a plot. So it's POST requests to my API but when I click submit, I have another function (which is a GET request) that retrieves the garden and the plots, this function works but I have to reload the page for them to appear: it's not added automatically. How can I do this?
I tried putting in the array of useEffect (which repeats my function to retrieve data from the Garden and the plots) the variable garden and plot or setGarden and setPlot but it doesn't work.
Here is the code of the GET Request :
const [garden, setGarden] = useState([]);
const [plot, setPlot] = useState([]);
const [loading, setLoading] = useState(false);
const gardenData = async () => {
setLoading(true);
const user = await AsyncStorage.getItem('user');
const parsedUserData = JSON.parse(user);
try {
const response = await axios.get(
`http://127.0.0.1/api/garden?user=${parsedUserData.user.id}`,
{
headers: {
Authorization: `Token ${parsedUserData.token}`,
},
},
);
if (response.status === 200) {
setGarden(response.data);
setLoading(false);
try {
const plotResponse = await axios.get(
`http://127.0.0.1/api/plots?garden=${response.data[0].id}`,
{
headers: {
Authorization: `Token ${parsedUserData.token}`,
},
},
);
if (plotResponse.status === 200) {
setPlot(plotResponse.data);
}
} catch (e) {
alert(e);
}
}
} catch (e) {
console.log('Erreur ' + e);
setLoading(false);
}
};
useEffect(() => {
gardenData();
}, []);
Thanks for the help !
I am assuming that the mentioned code is a part of an existing component with a return. It would be great to see where you run the POST method.
The useEffect hook gets triggered only on the mount of the component (as defined with the second argument - [])
So you need to call the gardenData function after successfully performing the POST request. After that, the data will refresh and state will take care of the rest.
Related
I have a react project setup with Redux and Axios. This is a function I am using to get data from an endpoint in my Redux actions:
export const getCSEfirstStageApplicants = () => async (dispatch) => {
try {
dispatch(LOADING());
const response = await axios.get(
`${baseUrl}/Franchisee/CSEFirstStageApplication`
);
if (response.status === 200) {
const { message, data } = response?.data || {};
return { message, data };
}
} catch (error) {
const { message } = error?.response?.data || {};
return message;
} finally {
dispatch(STOP_LOADING());
}
};
My component looks something like this:
import { useState, useEffect } from "react";
import {getCSEfirstStageApplicants} from "../../../redux/user/actions";
import { useDispatch } from "react-redux";
const MyComponent = () => {
const [cseApplicants, setCseApplicants] = useState([]);
const dispatch = useDispatch();
const getFirstStage = async () => {
const response = await dispatch(getCSEfirstStageApplicants());
if (response && response.data) {
console.log(response);
setCseApplicants(response.data);
return;
}
setCseApplicants([]);
};
useEffect(() => {
getFirstStage();
}, [dispatch]);
}
Apparently, this is working fine on my localhost. But when I build the app and push it to the server, it is giving an error on Chrome and Firefox and is working on Edge (browsers I have tested), indicating that response is undefined.
Chrome shows this error:
Firefox shows this error:
At first I thought it was the way the network call was made as preflight seemed to come after the xhr request. But checking Chrome showed that wasn't the error.
Another indication was an error that showed up as asyncgenerator error. I haven't been able to find a relation with this.
add properties to the empty object
const { message, data } = response?.data || {data:[], message:''};
I am trying to pass two parameters in createProductReview action when submitHandler function runs.
One is id and other one is an object containing two items. The problem is that I am unable to pass the object in createProductReview action. It gives undefine when I console log it in reducer function. I want to know how can I pass these two arguments without getting error.
Please check out attached image for error
submitHandler function
const submitHandler = (e) => {
e.preventDefault();
dispatch(createProductReview({ id, { rating, comment } }));
};
createProductReview
export const createProductReview = createAsyncThunk(
'reviewProduct',
async ({ productId, review }, thunkAPI) => {
console.log(productId, review);
try {
const {
userLogin: { userInfo },
} = thunkAPI.getState();
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`,
},
};
await axios.post(`/api/products/${productId}/reviews`, review, config);
} catch (error) {
const newError =
error.response && error.response.data.message
? error.response.data.message
: error.message;
return thunkAPI.rejectWithValue(newError);
}
}
);
In javascript, you need to pass keys to the object so it should be like this
dispatch(createProductReview({ productId:id, review:{ rating, comment } }));
Specially, when you are destructuring it in the function. Since destructure works by getting the object with its key.
so for example:
const x = {temp:"1"}
const {temp} = x;
console.log(temp);
//1
I have a problem (in React):
After the fetch occurs, when I console.log the User_ID value it shows me the right updated id value which is 1, but when I set the state of UserID and then I console.log it, it still shows me the default value which is 0 and not the updated one which equals to 1.
Can someone please take a look in my code and tell me where the problem is? And if the problem is delay, can someone tell me how to fix it?
The code:
export default function Login() {
const [Email, setEmail] = useState("");
const [UserID, setUserID] = useState(0);
const [Password, setPassword] = useState("");
const navigate = useNavigate();
const btnLogin = async () => {
console.log(1);
console.log(Email + "," + Password);
let s = await checkUserDetails(
Email,
Password
);
console.log("returned value=" + s + "and id = " +s.User_ID); //here the it returns the right User ID ( id = 1)
setUserID(s.User_ID) // for some reason here it doesnt update the right value of the User ID
if (s != null)
{
console.log("user_id is =" + UserID); // here it still prints User ID = 0 .. why?
alert("Logged In Successfully!!");
navigate('/')
console.log("h1");
} else {
console.log("err login!");
alert("failed to login");
}
};
const checkUserDetails = async (Email, Password) => {
console.log("we are in func checkUserDetails, email = " + Email);
try {
let result = await fetch(url + `?email=${Email}&pass=${Password}`, {
method: "GET", // 'GET', 'POST', 'PUT', 'DELETE', etc.
headers: new Headers({
"Content-Type": "application/json",
Accept: "application/json",
}),
});
let resultData = result.json();
console.log("meowwww");
console.log(resultData);
if (resultData) {
return resultData;
} else {
alert("unable to login");
}
} catch (e) {
console.log("Error Login", e);
alert("lo mevin");
}
};
return (
<div className="login-form">
<button
type="button"
className="button-LoginSign"
onClick={btnLogin}
>
Login
</button>
</div>
Thanks for all the helpers
that is because react state updates are not synchronous !!!!!
if you want to use the latest value inside the function where you are using you can directly use the response or create a useEffect with userId as a dependency
also your variable names should be camelCase - password, userId etc.
useEffect(()=> { this effect will have the latest value of ID}, [userId]
and will run whenever setUserId is called
The UserId piece of state will not show the updated value until the next render of that component. So in that initial run through the btnLogin function it still has the original value. Use s.User_ID there again if you need to. But I don't see that you really need access to that value other than a console log.
If you need to do something once that UserId has been set to state, put that inside a useEffect
useEffect(() => {
if (UserId > 0) {
// do something with id
}
}, [UserID])
You can never get your updated state this way. Because, you are writing console statement write after setUserId() hook, Which is asynchronous in nature. To get the updated value you need to write a useEffect hook with the state in dependency array like this. So whenever you set UserID the updated state will be reflected in this useEffect.
useEffect(()=>{
console.log(userId)
//Now desired computation on base of updated state
},[userId])
I have an App.js file that contains a form that when on submitted, causes triggers a state change to render a new page. I'm trying to create a mock Jest test that does these steps:
Take mock data
Sends a POST request like addInfo is doing
Checks if "DONE WITH FORM" is rendered onto the screen.
I also had an idea that we could just fill out a form that takes in the valid_address and valid_number and click a button that triggers the addInfo function to run with the information passed in however I'm unsure of that method and it leads me to a CORS error.
From what I've seen on the web, I think mocking this addInfo using Jest and then testing what is rendered is the best way to go however I'm completely stuck on building this test.
Here's what I have for my App.js
const addInfo = async (formInfo) => {
try {
let data = {
valid_number: formInfo.validNumber,
valid_address: formInfo.validAddress
}
let addUserUrl = process.env.REACT_APP_URL +'/verify'
let addUserData = await fetch(
addUserUrl,
{
method: "POST",
headers: {
"Content-type": "application/json",
"x-api-key": process.env.REACT_APP_KEY
},
body: JSON.stringify(data)
}
)
if (addUserData.status !== 200) {
throw 'Error adding User'
}
let addUserDataJson = addUserData.json()
let ret = {
added: true,
}
return ret
} catch (error) {
console.log('Error')
let ret = {
added: false,
}
return ret
}
}
const onFinish = async (values: any) => {
console.log('Transaction verified');
let addStatus = await addInfo({
validNumber: "123434",
validAddress: "D74DS8JDSF",
})
if (promoStatus.added) {
setState({
...state,
showPage: false
})
} else {
setState({
...state,
showPage: true
})
}
};
return (
{!state.showPage &&
<>
<div>
<p>
DONE WITH FORM
</p>
<div>
</>
}
)
Here's what I've tried in App.test.js:
it('DONE WITH FORM APPEARS', async() =>{
// Render App
const { getByPlaceholderText, queryByText, getByText } = render(<App />);
// Entering Valid Number
const validNumberInputBox = getByText('Enter Valid Number);
fireEvent.change(validNumberInputBox, { target: { value: "123434" } });
expect(validNumberInputBox).toHaveValue("123434");
// Entering Valid Address
const validAddressInputBox = getByText('Enter Valid Address');
fireEvent.change(validAddressInputBox, { target: { value: "D74DS8JDSF" } });
expect(validAddressInputBox).toHaveValue("D74DS8JDSF");
// Button Click
userEvent.click(screen.getByRole('button', {name: /Submit/i}));
//Check if the DONE WITH FORM is shown
expect(await waitFor(() => getByText('DONE WITH FORM'))).toBeInTheDocument();
});
I've tried almost everything I could find through other stack overflow posts and web articles. so I'd really appreciate any help on how to implement this unit test.
The first step would be to mock the async function performing the POST request (addInfo). You never want to try real HTTP requests in unit tests (this won't work since Jest runs in a Node environment where fetch or XMLHttpRequest APIs are not implemented). Beside this, component/unit tests should be independent from any other system like a backend exposing APIs.
To do so, your async function should be in a separate file (so a JS module), then you could mock this module using Jest :
// api.js
export const addInfo = () => {...}
// App.test.js
import * as Api from 'api.js';
// here you can define what your mock will return for this test suite
const addInfoSpy = jest.spyOn(Api, 'addInfo').mockResolvedValue({ ret: true });
describe('...', () => {
test('...', async () => {
// perform user interactions that should trigger an API call
expect(addInfoSpy).toHaveBeenCalledWith('expected addInfos parameter');
// now you can test that your component displays "DONE WITH FORM" or whatever
// UI it should be displaying after a successful form submission
});
});
https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvaluevalue
https://jestjs.io/docs/jest-object#jestspyonobject-methodname
https://jestjs.io/docs/expect#tohavebeencalledwitharg1-arg2-
I have create a log in page, which has to enter username and password to authenticate. After the authentication, I push the history to the Homepage by using react-router and set a cookie. The code is like:
const handleLogin = () =>{
const options = {
url: "http://test/login",
method: "POST",
withCredentials: true,
headers:{
"Accept": "application/json",
"content-Type": "application/json;charset-UTF-8"
},
data: {
"username": user,
"password": pass,
}
};
Axios(options).then(Response =>{
if(Response.data.JWT){
let decode = jwt_decode(Response.data.JWT);
if(decode.id){
setLoginUser(decode.username);
setAuthenticated(true);
history.push('/Homepage');
Cookies.set("isLogged", {expires: 1} )
}
}else{
alert("Invalid user");
}
});
}
I also used JWT from back-end, here the application is fine. But when I refresh the web page, I check through the cookies for the refresh token to stay in the Homepage, and remove it for log out.
const readCookie = () => {
let user = false;
if(user = Cookies.get("isLogged")){
history.push('/Homepage');
setAuthenticated(true);
}else{
history.push('/');
}
}
useEffect(() => {
readCookie()
},[]);
Is it OK to get refresh token like this?
Your function, on the outside, should be a dependency of your useEffect. To avoid that, just move it inside the hook. Other than that, looks good to me. Reading a cookie is a side-effect, so you're doing it the right way.
useEffect(() => {
const readCookie = () => {
let user = false;
if(user = Cookies.get("isLogged")){
history.push('/Homepage');
setAuthenticated(true);
}else{
history.push('/');
}
}
readCookie()
},[]);
If you wanted to leave readCookies outside of the hook, you'll have to wrap it in a useCallback hook and use that in your dependency, like so:
const readCookiesCallback = useCallback(() => {/* your read cookies code */}, [])
useEffect(() => {
readCookiesCallback()
}, [readCookiesCallback])
For what you're doing, that's unnecessary. My first example is the way to go.