hey guys i dont know why my setPlaceId works but not my setPlaceName
placeName doesnt print anything when there is some data that should be printed out could someone help? So yea i added more code, if you need more things ill post it, thanks for everyones help in advance i wish i can fix this as soon as possible.
tldr: setPlaceId pastes text but setPlaceName doesnt paste text when there are info that should be pasted.
btw i deleted some stuff from the code so it wouldnt be so long so dont worry about me not having some stuff that i called
function ConvertPlaces() {
const [placeName, setPlaceName] = useState("");
const [placeId, setPlaceId] = useState("");
useEffect(() => {
convert();
}, []);
const convert = () => {
fetch(
`https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=${userInput}`,
{
method: "GET",
headers: {
//deleted api keys
},
}
)
.then((res) => res.json())
.then((response) => {
console.log(response);
setPlaceName(response.Places.map((airport) => airport.PlaceName));
setPlaceId(response.Places.map((airport) => airport.PlaceId));
})
.catch((err) => {
console.log(err);
});
};
const handleChange = (event) => {
setInputField(event.target.value);
setUserInput(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
setSavedInput(inputField);
setInputField("");
convert();
};
return (
<div>
<SearchPlaces
run={convert}
handleChange={handleChange}
handleSubmit={handleSubmit}
inputField={inputField}
userInput={userInput}
savedInput={savedInput}
/>
<p>sfdajfp</p>
<p>{placeId}</p>
<p>{placeName}</p>
</div>
);
}
export default ConvertPlaces;
I am not sure this is going to solve your issue, as you should really show a little bit more code but, regardless of that, may I suggest you loop through the response only once?
// ...
fetch([API endpoint])
.then(res => res.json())
.then(response => {
const setPlaceData = response.reduce((outObj, airport) => {
outObj.PlaceNames.push(airport.PlaceName);
outObj.PlaceIds.push(airport.PlaceId);
return outObj;
}, { PlaceNames: [], PlaceIds: [] });
/*
* I am assuming that you are using 'useState()' hook and
* you are willingly replacing whatever content is already
* present in your state variables rather than adding to it.
* E.g.
* setPlaceName([...placeName, ...setPlaceData.PlaceNames]);
*/
setPlaceName(setPlaceData.PlaceNames);
setPlaceId(setPlaceData.PlaceIds);
});
// ...
Related
There is the following situation - when a certain page is opened, several requests are made through useEffect. These requests go in parallel to each other and are used by the interceptor. In case of an error on request, we render it with a pop-up window using react-toastify. All is well until a certain access error arrives. In this case, 3 windows are rendered with the same error, since the requests were sent at the same time. The task is to visualize this error only once, instead of 3. At the same time, it is necessary to maintain the asynchrony of requests. How could such a thing be implemented? Or maybe I don’t need to touch the requests themselves, but I need to think over the architecture for showing errors? I will be glad to any advice!
Below is a code example.
const instance = axios.create({
baseURL: BASE_URL,
headers: {
"Content-Type": "application/json",
"Access-Control-Expose-Headers": "Content-Disposition",
},
timeout: 3600000,
});
instance.interceptors.request.use(
(config) => {
const configDes = { ...config };
const token = 12345678;
if (token) {
configDes.headers.Authorization = `Bearer ${token}`;
}
return configDes;
},
(error) => Promise.reject(error)
);
instance.interceptors.response.use(
(response) => response,
(error) => Promise.reject(error)
);
const api1 = () => instance.get("api/api1");
const api2 = () => instance.get("api/api2");
const api3 = () => instance.get("api/api2");
const api1Action = () =>
api1()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const api2Action = () =>
api2()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const api3Action = () =>
api3()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const Component = () => {
useEffect(() => {
dispatch(api1Action());
dispatch(api2Action());
dispatch(api3Action());
}, []);
// ToastContainer show notifyError
return (
<ToastContainer /> )
}
I tried to think over an option related to saving the status of the error, but I realized that this would bring problems in the future, because suddenly, immediately after receiving an access error, once the user tries to go somewhere else where there will be the same error. In this case, the error will not be rendered since I saved it before and track this error so as not to show the error window.
The react-toastify library allows you to set a toast element with a toastId value. If two containers have the same ID, then only one container will appear. The library itself keeps track of duplicates of its containers.
const notifyError = (errorMessage: string | Error) =>
toast.error(errorMessage, {
position: "bottom-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
toastId: errorMessage as string,
});
Please use Promise.all when you invoke multiple async requests (assuming you would like to stop if even one request fails)
const api1 = () => instance.get("api/api1");
const api2 = () => instance.get("api/api2");
const api3 = () => instance.get("api/api2");
const Component = () => {
useEffect(() => {
dispatch(Promise.all([ api1(), api2(), api3() ]).then(results = > {
// so whatever needed - results is an array of responses
}).catch(notifyError));
}, []);
return (
<ToastContainer /> )
}
More about Promise.all and Promise.allSettled can be found here
EDIT (after more details shared in comment):
const Component = () => {
useEffect(() => {
const apiCalls = [
instance.get("api/api1"),
instance.get("api/api2"),
instance.get("api/api2"),
];
dispatch(Promise.allSettled(apiCalls).then(results = > {
const errors = results.filter(result => result.status == 'rejected').map(result => result.reason);
// decide here if one of the errors break your logic
const values = results.filter(result => result.status == 'fulfilled').map(result => result.value);
// if all ok - use values
}));
}, []);
return (
<ToastContainer /> )
}
I call the function inside of useEffect. This means the function is not repeatedly called but for some reason it seems as if the data is being removed after a short period of time
const [following, setFollowing] = useState([])
useEffect(() => {
getUser()
getFollowing()
}, []);
The function here fetches all of the users which the current user is currently following. It seems to work fine and console logging the follower's array returns the users which are expected and appear to render on the screen.
async function getFollowing()
{
const followerRef = query(collection(db, "followers"), where("follower", "==", auth.currentUser.uid));
const querySnapshot = await getDocs(followerRef);
let followerArray = []
querySnapshot.forEach((doc) => {
const storageRef = ref(storage, (doc.data().followingEmail) + "/pp");
getDownloadURL(storageRef).then((url) => {
followerArray.push([doc.data().followingEmail, url, doc.data().following]);
}).catch((error) => {
followerArray.push([doc.data().followingEmail, musicIcon, doc.data().following]);
// setFollowing(followerArray)
});
});
console.log("follower array");
console.log(followerArray);
setFollowing(followerArray)
// console.log(following)
}
This is what I am using to loop through all of the elements. The elements do appear in order and formatted correctly on the page but once they have loaded in they somehow disappear. I am not sure what is causing this. Maybe I should make the page wait until the data has loaded in from firebase. Maybe I should have some conditional statement that stops the data from disappearing.
{following.map((f) =>
<Row key={f[2]} className="hover">
<Col xs={2}>
<div className="">
<img src={f[1]} className="smallPhoto" />
</div>
</Col>
<Col xs={10}className="">
<p className= "followText">{f[0]}</p>
</Col>
</Row>
)}
here is what the output looks like for me when refreshing the page
https://giphy.com/gifs/uDtDpY198yCRn6S2CW
I have tried many solutions but none of them are seeming to work.
you are throwing promises inside the foreach, and seting empty array into the state, you should await for the promises to have something inside the array
//this fn should be in another file.
async function getFollowing() {
const followerRef = query(coll(db, "followers"), where(...));
const querySnapshot = await getDocs(followerRef);
let followerArray = [];
const promises = querySnapshot.map((doc) => {
const storageRef = ref(storage, `${followingEmail}/pp`);
// this part is hurting my eyes, xD
// why you are adding [] instead of {} into the array?
return getDownloadURL(storageRef)
.then((url) => followerArray.push([fields]))
.catch((error) => followerArray.push([fields]));
});
});
await Promise.all(promises);
console.log("follower array", followerArray);
return followerArray;
}
//inside your component
const YourComponent = () => {
const [following, setFollowing] = useState([]);
useEffect(() => {
getUser();
getFollowing().then(array => setFollowing(array));
}, []);
};
Thank you so much for the support. The program seems to be working as intended now. The change in useEffect I believed made a key difference. This is what my code looks like now:
}, []);
useEffect(() => {
getUser()
getFollowing().then(array => setFollowing(array));
getFollowers()
}, []);
Changes in getFollowing function that helped to resolve the issue
let getFollowing = async () =>
{
const followerRef = query(collection(db, "followers"), where("follower", "==", JSON.parse(localStorage.getItem("userID"))));
let followerArray = [];
await getDocs(followerRef).then((querySnapshot) =>{
querySnapshot.forEach((doc) => {
const storageRef = ref(storage, (doc.data().followingEmail) + "/pp");
getDownloadURL(storageRef)
.then((url) => { followerArray.push([doc.data().followingEmail, url, doc.data().following]);
})
.catch((error) => {followerArray.push([doc.data().followingEmail, musicIcon, doc.data().following]);
});
})
} )
console.log("followerArray", followerArray)
return followerArray;
}
I hope you all are good. I am trying to create an update function in Mern, when I tried it on Postman it works fine but when I 'am trying to implement same with React it does not give me any result and also not giving me any error. I 'am unable to figure out What I'am doing wrong. If anyone here can figure out by looking on my code, what is wrong with it, It will be great help. Thanks a lot in advance.
import { getANews, updateNews } from "../../actions/news";
const Edit = ({ router }) => {
const [websiteCategory, setWebsiteCategory] = useState("");
useEffect(() => {
initNews();
}, [router]);
// get a single news to make changes on it
const initNews = () => {
if (router.query._id) {
getANews(router.query._id).then((data) => {
if (data.error) {
console.log(data.error);
} else {
setWebsiteCategory(data.websiteCategory);
}
});
}
};
const onChangeWebsiteCategory = (event) => {
setWebsiteCategory( event.target.value);
};
// update the category field in this particular news
const editNews = (event) => {
event.preventDefault();
updateNews(router.query.id, websiteCategory).then((data) => {
if (data.error) {
console.log("error");
} else {
console.log(websiteCategory);
console.log(data);
}
});
};
return (
<div>
<h3>Edit News</h3>
<form onSubmit={editNews}>
<div className="form-group">
<label>Website-Category </label>
<input
type="text"
className="form-control"
defaultValue={websiteCategory}
onChange={onChangeWebsiteCategory}
/>
</div>
<div className="form-group">
<input type="submit" value="Edit News" className="btn btn-primary" />
</div>
</form>
</div>
);
};
export default withRouter(Edit);
Edit: Thanks for the reply, below is the complete code. Hope this helps you helping me :)
This is my action/news file
import fetch from "isomorphic-fetch";
import { API } from "../config";
// API = http://localhost:3000/
export const getANews = (id) => {
return fetch(`${API}/news/${id}`, {
method: "GET",
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
export const updateNews = (id) => {
return fetch(`${API}/news/${id}`, {
method: "POST",
headers: {
Accept: "application/json",
},
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
your code looks fine. I do not have your ....action/news implementation. So can't run your code.
may I suggest your line 25-26 be switched to
setWebsiteCategory(event.target.value);
you code in my IDE
I looked at your code have repeated your problem. useEffect 2nd parameter should be an empty array. so the initialization is called once in the begining. (read "Preventing Endless Callbacks using Dependencies" section of https://blog.bitsrc.io/fetching-data-in-react-using-hooks-c6fdd71cb24a
see https://codepen.io/lukelee10/pen/VweGByb
is what i have jested up. the getANews and updateNews from line 46 - 53 are my stubbed out call to the API (notice they are both async function(meaning they return promise).
SO, your take away is line 54 - 97.
Finally I found the solution, earlier I was not sending category in my update function. So I removed my update function from action file and fetch the data in my function and send the updated data back in the body.
const editNews = () => {
return fetch(`${API}/news/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(websiteCategory),
})
.then((response) => {
return response.json();
})
.catch((err) => console.log(err));
};
I am creating a simple quiz with persisting response state and i am currently stuck trying to figure out why responseState is undefined in my handleAnswerClick function, this function is triggered much later if at all per click. By then the States should all be set.
const Question: React.FC<IProps> = (props) => {
const [responseState, setResponseState] = useState([]);
const [question, setQuestion] = useState<IStateProps>(props.id);
const [answers, setAnswers] = useState([]);
const [choice, setChoice] = useState();
const initialResponseState = () => {
const data = {
duration: '00:01:00',
examTakerProgress: 1,
question: props.id,
answer: '1',
}
axios.post(`${host}/api/responseState/`, data)
.then(() => {
console.log('Created the initial Response State');
})
.catch((err) => {
console.log(err);
})
}
const getData = async () => {
await axios.all([
axios.get(`${host}/api/question/${question}`),
axios.get(`${host}/api/responseState/examTakerProgress/1/question/${props.id}`)
])
.then(axios.spread((questionData, responseData) => {
if (!responseData.data.length > 0) {
initialResponseState();
}
setResponseState(responseData.data);
setQuestion(questionData.data);
setAnswers(questionData.data.answers);
setChoice(responseData.data.length > 0 ? responseData.data[0].answer : '');
setTimeout(() => {
props.toggleLoading();
}, 50);
}))
.catch(err => console.error(err));
};
useEffect(() => {
getData()
}, [])
const handleAnswerClick = async (number: number) => {
setChoice(number);
const data = {
duration: '00:01:00',
examTakerProgress: 1,
question: props.id,
answer: number,
}
await axios.put(`${host}/api/responseState/${responseState[0].id}/`, data)
.then((data) => {
console.log(data);
console.log('choice changed to :' + number);
})
.catch((err) => {
console.log(err);
})
}
if (props.loading) {
return <Loader/>
}
return (
<React.Fragment>
<h3>{question.label}</h3>
<p className='mb-3'>{question.description}</p>
<ListGroup as="ul">
{answers.map((answer, index) => {
if (answer.id === choice) {
return <ListGroup.Item key={answer.id} action active> <Answer key={index}
answer={answer}/></ListGroup.Item>
} else {
return <ListGroup.Item key={answer.id} action onClick={() => {
handleAnswerClick(answer.id)
}}><Answer key={index}
answer={answer}/></ListGroup.Item>
}
}
)}
</ListGroup>
</React.Fragment>
)};
Can someone explain me the reason why this is happening?
Based on this line
const [responseState, setResponseState] = useState({});,
the responseState is an object.
but in the handleAnswerClick function you are using it as an array ${responseState[0].id}.
Note that this actually works if the object has a 0 key but since you're getting undefined that is not the case.
Reasons why responseState is undefined.
The most obvious is that you set responseState to undefined
The only place you call setResponseState is in getData where you have setResponseState(responseData.data);, so please check if responseData.data isn't undefined.
You never get to call setResponseState and the initial state is an object but you try to get responseState[0].id
I'm not sure what data type you are handling, but if you defined const [responseState, setResponseState] = useState({}); with the default value of useState as {} and then try to access responseState[0].id, there is two things happening.
Either you have an object with number keys or you have an array, but if you have an array, you should declare the initial value as an array.
But this isn't the problem, the problem is that you might never get to the part where you call setResponseState(responseData.data); and then you will have responseState to be {} and when trying to do responseState[0] it will be undefined, and when calling responseState[0].id you will get an error saying that you cannot read id of undefined.
Ways to fix this
Debug your code, make sure that the api call returns what you are expecting and setResponseState(responseData.data); is called.
Check if responseState[0] exists before try to access responseState[0].id
I would be able to help more, but you didn't add more information, so it's hard to help, but above is what I think it's the problem
Basically i am creating a responseState when a question loads for the first time, if a question is afterwards reloaded the State is already there since it is persisted. This means i also have to make sure to call setResponseState for the first case where the ResponseState still does not exist.
const initialResponseState = () => {
const data = {
duration: '00:01:00',
examTakerProgress: 1,
question: props.id,
answer: '1',
}
axios.post(`${host}/api/responseState/`, data)
.then(() => {
setResponseState(res.data)
console.log('Created the initial Response State');
})
.catch((err) => {
console.log(err);
})}
I'm sure it's a very simple problem but I've tried so many different things and I just make it worse. I'm building a nutrition app that has total calories. It's supposed to calculate total calories based on calories from api call and it works great after the second time. But the first time it shows 0
Nutrition Component
const [search, setSearch] = useState("");
const [isLoaded, setIsLoaded] = useState(false);
const [foodId, setFoodId] = useState("");
const [foodList, setFoodList] = useState([]);
const [totalCal, setTotalCal] = useState(0);
const addCal = (cal, totalCal) => {
const total = cal + totalCal;
setTotalCal(total);
};
// Getting user input for an argument to be used later in API call
const getInput = e => {
e.preventDefault();
setSearch(e.target.value);
};
// Submitting user input / starting API call / setting food to be searched
const handleSubmit = e => {
e.preventDefault();
setIsLoaded(true);
// console.log(list);
};
// Function that holds API call using form input
const getNutrition = food => {
const appId = "e124e5bc";
const appKey = "fd3e0319818416b4e8496e3502bcb565";
fetch(
`https://api.nutritionix.com/v1_1/search/${food}?&appId=${appId}&appKey=${appKey}`
)
.then(response => {
return response.json();
})
.then(responseData => {
let foodId = responseData.hits[0].fields.item_id;
fetch(
`https://api.nutritionix.com/v1_1/item?id=${foodId}&appId=${appId}&appKey=${appKey}`
)
.then(response => {
return response.json();
})
.then(responseData => {
setFoodId(foodId);
const foodObject = {
calories: responseData.nf_calories,
protein: responseData.nf_protein,
food: responseData.item_name,
key: foodId,
totalCal: totalCal
};
addCal(foodObject.calories, totalCal);
setFoodList([...foodList, foodObject]);
setIsLoaded(false);
});
});
console.log(isLoaded);
};
// Calling nutrition function that holds API
useEffect(() => {
if (isLoaded) {
getNutrition(search);
setFoodId(foodId);
}
// eslint-disable-next-line
}, [isLoaded]);
return (
<div className={styles.nutritionContainer}>
<FoodForm submit={handleSubmit} input={getInput} />
<table className={styles.center}>
<thead>
<tr>
<th>Food Item</th>
<th>Calories</th>
<th>Protein</th>
<th>Total Calories</th>
<th>Total Protein</th>
</tr>
</thead>
<tbody>
<TableList foodList={foodList} />
</tbody>
</table>
</div>
);
};
export default Nutrition;
List with dynamic table data from api call in Nutrition Component
const TableList = ({ foodList }) => {
return foodList.map(item => (
<tr key={item.key}>
<td>{item.food}</td>
<td>{item.calories}</td>
<td>{item.protein}</td>
<td>{item.totalCal}</td>
</tr>
));
};
export default TableList;
I think your code has a little problems with reusing same variable name again and check following code I fixed. When you use isLoaded value to default, Add search value to something as default.
I hope it will be helped. But for now, you api limit is over because i tested its alot. :P
FIX 1
useEffect(() => {
if (isLoaded) {
getNutrition(search);
}}, [isLoaded]);
FIX 2
const getNutrition (food)=> {
const appId = "e124e5bc";
const appKey = "fd3e0319818416b4e8496e3502bcb565";
fetch(
`https://api.nutritionix.com/v1_1/search/${food}?&appId=${appId}&appKey=${appKey}`
)
.then(response => {
return response.json();
})
.then(responseData => {
let fId = responseData.hits[0].fields.item_id;
setFoodId(fId);
fetch(
`https://api.nutritionix.com/v1_1/item?id=${fId}&appId=${appId}&appKey=${appKey}`
)
.then(response => {
return response.json();
})
.then(responseData => {
console.log("DATA", responseData);
const foodObject = {
calories: responseData.nf_calories,
protein: responseData.nf_protein,
food: responseData.item_name,
key: foodId,
totalCal: totalCal
};
addCal(foodObject.calories);
setFoodList([...foodList, foodObject]);
setIsLoaded(false);
});
});
console.log(isLoaded);};
FIX 3
const addCal = cal => {
const total = cal + totalCal;
setTotalCal(total);};
because isLoaded is set initially to false and therefore it will not call getNutrition(search); setFoodId(foodId); inside useEffect
I can't say for certain why your first api call isn't working but I have a few suggestions that night help.
First off I wouldn't use the useEffect hook at all. It unnecessarily complicates your code. You only need to call the api each time the form is submitted so why not just do it then? Instead of changing the is loaded value etc. You might find after doing that your error is gone, or at least it will be easier to understand what's happening in your code.
The other thing I would do is remove totalCals from state. Instead i would calculate it each time, unless you have thousands or hundreds of thounds of items it's really not going to be a performance issue. The benefits of this is that your code will be easier to understand and if you said the issue was with the calories not updating this should fix it. It also means your state isn't denormalised. If you calculate it every time and then remove an item from the array, you dont need to worry about forgetting to also reduce the total etc.
Good luck!!