I fetch data from an api and data takes successfully but there is a problem when I determine the data to an array.
Array doesn't assign the data.
const [Profile,setProfile]=useState([]);//I created array state here
const [username,setUsername]=useState('');
const [password,setPassword]=useState('');
const [logout,setLogout]=useState(0);
useEffect(() => {
return () => {
console.log(Profile);
}
}, [Profile.length])
const login = () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Access-Control-Allow-Origin","*");
myHeaders.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS');
var raw = JSON.stringify({username, password});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost/scheduleapp/api/personals/login", requestOptions)
.then(response => response.text())
.then(response =>{setProfile(response) ;console.log(response); localStorage.setItem('Logged',response)})//here response is seen console and setted to local storage with success but setstate doesnt work
.catch(error => console.log('error', error));
}
As I say setstate doesnt set data to array
If you want to add an array to your state. First you can give that state an empty array as an initial value.
Example -
const [ data, setdata] = useState([])
After that you can destructor your response and assign them one by one to the array like this
setdata(... response)
Make sure the response is an array.
use response.json() instead of response.text() and for storing the response in the localStorage use localStorage.setItem(JSON.stringify(response))
The problem with the profile state, you need to renamed it to 'profile' instead of 'Profile' because React assuming is a component for the capitalization, that's why it never assigns the value to the state.
for Example:
const [profile,setProfile]=useState([]);
instead of this:
const [Profile,setProfile]=useState([]);
Other important detail is how you are using the 'useEffect' for example when you are console.log the results is when the component is unmounted, to make sure you are setting the value put it outside the useEffect to see if the state is changing like this:
useEffect(() => {
return () => {
console.log(Profile);
}
}, [Profile.length])
use like this:
useEffect(() => {
// call your function here if you want to execute when the component is mounted.
}, []);
console.log(profile);
Related
I have an API called getQuote and a component called QuoteCard. Inside QuoteCard I'm trying to render an array of users that liked a quote. The API works fine, I have tested it, and the code below for getting the users works fine too.
const Post = async (url, body) => {
let res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"accept": "*/*"
},
body: JSON.stringify(body)
}).then(r => r.json());
return res;
}
const getAllLikes = async () => {
let users = await Post('api/getQuote', {
id: "639e3aff914d4c4f65418a1b"
})
return users
}
console.log(getAllLikes())
The result is working as expected :
However, when trying to map this promise result array to render it onto the page is where I have problems. I try to render like this:
<div>
{getAllLikes().map((user) => (
<p>{user}</p>
))}
</div>
However, I get an error that states:
getAllLikes(...).map is not a function
I don't understand why this is happening. Why can't I map the array? Is it because it's a promise or something?
And if anyone needs to see the getQuote API, here it is:
//Look ma I wrote an API by myself! :D
import clientPromise from "../../lib/mongodb";
const ObjectId = require('mongodb').ObjectId;
import nc from "next-connect";
const app = nc()
app.post(async function getQuote(req, res) {
const client = await clientPromise;
const db = client.db("the-quotes-place");
try {
let quote = await db.collection('quotes').findOne({
_id: new ObjectId(req.body.id)
})
res.status(200).json(JSON.parse(JSON.stringify(quote.likes.by)));
} catch (e) {
res.status(500).json({
message: "Error getting quote",
success: false
})
console.error(e);
}
})
export default app
Thanks for any help!
It is due to the fact that getAllLikes is an async function and thus it returns promise which does not have a map function.
You can either save it in a state variable before using await Or chain it with .then.
Minimal reproducible example which works
const getAllLikes = async () => {
return ['a', 'b']
}
getAllLikes().then((r) => r.map((g) => { console.log(g) }))
Edit: The above code won't work if directly used with jsx since the return of getAllLikes will still be a promise. Solution would be to save it in a state variable and then using it.
I am from Angular and I believe we call pipe on Observables (or Promises). Map can then be called inside the pipe function
observable$ = getAllLikes().pipe(map( user => <p>{user}</p>))
If there is no pipe, I can only think of manually subscribing (which is not a good practice)
sub$ = getAllLikes().subscribe( user => <p>{user}</p>)
// unsub from sub$ appropriately
// We do this from ngOnDestroy in angular
ngOnDestroy() {
this.sub$?.unsubscribe()
}
App.js:
function App() {
const [items, setItems] = useState([]);
useEffect(() => {
const searchDB = () => {
fetch("http://127.0.0.1:8443/subColumns/5/?key=fc257229-8f91-4920-b71f-885403114b35", {
mode: 'cors',
credentials: 'include'
})
.then(res => res.json())
.then((json) => {
setItems(json);
})
console.log({items});
}
searchDB();
}, [])
I need to keep the json response in a state varibale because in the future, the API request will nt be hard coded and I expect the user will make multiple API requests without refreshing, and the results will have to be mapped to different components. At the moment, trying to print {items} to the console returns an empty array.
Since setItems is the asynchronous method, you can't get the updated value immediately after setItems. You should use another useEffect with dependency to see the value.
useEffect(() => {
console.log(items);
}, [items]);
Does anyone know why this fetch continues to fire. I have also tried putting it inside a useEffect with no luck. It should only fire once to return once imdbID has loaded.
const WatchOnList = ({ imdbId }) => {
const [locations, setLocations] = useState([])
var headers = new Headers();
headers.append("x-api-key", "API_KEY")
var requestOptions = {
method: 'GET',
headers: headers,
crossDomain: true,
redirect: 'follow'
};
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then((res) => {
setLocations(res)
console.log(locations)
})
.catch(error => console.log('error', error));
}
fetchData();
With the current structure, the request will fire on every re-render. Which will be quite often in a React app. useEffect is the right place for such a function. But there are some caveats:
You can't make useEffect async, you have to create an async function inside the hook instead and call it afterward.
useEffect will per default run on every update, so you have to tell it explicitly to only run once (like componentDidMount for class components). This can be done by passing an empty array as the second parameter. The hook watches parameters specified in this array and only updates when one of them changes. As it is empty, it only fires once on initialization.
This should work:
useEffect(() => {
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then(res => {
setLocations(res);
console.log(locations);
})
.catch(error => console.log("error", error));
}
fetchData();
}, []);
Read more about the behavior of hooks here and here.
I'm struggling with the concept of Promise in JavaScript. I'm writing a React app that makes a GET call out to a separate API service in Java, and I want to store its state in a useState() hook. So here's my fetch code:
const ratingsUrl = "%URL%";
const base64 = require("base-64");
const login = "login";
const password = "password";
function fetchRatings() {
return fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
}),
})
.then((response) => response.json())
.catch(handleError);
}
And now I'm trying to store its state in a hook in my page component:
function DisplayPage(){
const [ratings, setRatings] = useState(fetchRatings());
.
.
.
}
Now, the data returns but it's in a Promise, hence causing errors down the line:
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(20)
What I need to do is to initialise the data in a hook and return it in a Table so I can map through it. However, whenever I try to do something like
ratings.map()
I get a TypeError in the console saying ratings.Map is not a function.
I'm aware that the fetch library returns data asynchronously, but all I really want is for the PromiseResult to be stored in a useState() hook so I can perform operations on it further down the line.
async methods return promises. If you directly set the result of a promise in your setRatings state variable, you will get a promise.
Typically this would be rewritten something like this:
function DisplayPage(){
const [ratings, setRatings] = useState(null);
useEffect(() => {
fetchRatings
.then(result => setRatings(result))
.catch(err => console.error(err));
}, []);
if (ratings === null) return <div>loading...</div>;
/* .. do your thing .. */
}
How About this ,
const [ratings, setRatings] = useState();
useEffect(()=>{
fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
})}).then((response) => {let res = response.json();
setRatings(res)
})
.catch(handleError);
},[])
I would advice using the useEffect hook to set initial state.(similar to componentDidMount)
So if the response you expect is for example an array.
const [ratings, setRatings] = useState([]);
Then in the useEffect hook, update state when you get a response from your fetch request.
That way you can prevent errors if you for example map over ratings in your DOM somewhere before the request is finished.
useEffect(){
fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
}),
})
.then((response) => {
response.json()
})
.then(res => setRatings(res))
.catch(handleError);
Because the fetch runs asynchronously, you're not going to be able to initialize your state using the immediate result of invoking fetchRatings.
There are, fortunately, a couple of fairly straightforward ways to handle this. You can initialize your state with an empty value and then update it once fetchResults resolves:
function DisplayPage() {
// initially ratings will be undefined
const [ratings, setRatings] = useState();
// update state when fetchResults comes back
fetchResults().then(response => setRatings(response));
// ...
The above example omits this in the interest of readability, but you'd generally do this via useEffect so it runs when your component mounts or when relevant inputs (usually props, known as dependencies for the effect) change:
function DisplayPage() {
const [ratings, setRatings] = useState();
useEffect(() => {
fetchResults().then(response => setRatings(response));
}, []) // empty dependencies array will cause the effect to run only once, on mount
// ...
How would I assign the id of the current user to a newly created object? I've tested the server and it seems to be working as I intend on the back-end so it must be an issue with my React/Redux flow.
I'm currently assigning the userId in submit handler on the form then passing it to the action and reducer. I've copied my code here for clarification.
Submit handler when for when a new object gets created
handleOnSubmit = event => {
event.preventDefault();
let newTeam = Object.assign({}, this.props.teamFormData, {
author: this.props.auth.user.userId
});
this.props.createTeam(newTeam);
};
Action
export const createTeam = team => {
return dispatch => {
return fetch(`${API_URL}/teams`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(team)
})
.then(response => response.json())
.then(team => {
dispatch(addTeam(team));
dispatch(resetTeamForm());
})
.catch(error => console.log(error));
};
};
Reducer
case "CREATE_TEAM_SUCCESS":
return state.concat(action.teams);
Somewhere along this flow, my object assign for userId starting from the React component is failing. Should I be thinking about doing this differently?
Any help will be appreciated.