Set an empty array in state - reactjs

In my application I have a initial state that start with some values.
In a process, I need to change this state for a empty array, (that is a return of my Api).
I am trying do this, but the value of the state don't change.
What can I do?
My code
import React, {useEffect, useState} from "react";
import "./style.scss";
import Services from "../../services/Services";
export default function Acoplamento({history, match}) {
const [veiculos, setVeiculos] = useState([]);
const [loading, setLoading] = useState(false);
const [type, setType] = useState(1);
const getAllVeiculos = async () => {
setLoading(true);
await Services.VeiculoServices.getAll().then(result => {
setVeiculos(result.data); // Here, i have a array with some objects
}).catch(error => {
error(error.message);
}).finally(() => setLoading(false));
return true;
}
const getOneVeiculos = async () => {
setLoading(true);
await Services.VeiculoServices.get().then(result => {
setVeiculos(result.data); // Here, my return is a empty array, but my state don't chage
}).catch(error => {
error(error.message);
}).finally(() => setLoading(false));
return true;
}
useEffect(() => {
if (type === 1) {
getAllVeiculos();
}
if (type === 2) {
getOneVeiculos();
}
}, [type]);
return (
<div>
<button onClick={() => setType(2)}>Click</button>
{veiculos.map(item => (
<div>{item.name}</div>
))}
</div>
);
}

I guess the problem arises because you did not convert the incoming data to json format. It can solve the following code problem.
import React, {useEffect, useState} from "react";
import "./style.scss";
import Services from "../../services/Services";
export default function Acoplamento({history, match}) {
const [veiculos, setVeiculos] = useState([]);
const [loading, setLoading] = useState(false);
const [type, setType] = useState(1);
const getAllVeiculos = async () => {
setLoading(true);
const response = await Services.VeiculoServices.getAll();
const result = await response.json();
if (result.data) setVeiculos(result.data);
setLoading(false);
}
const getOneVeiculos = async () => {
setLoading(true);
const response = await Services.VeiculoServices.get();
const result = await response.json();
if (result.data) setVeiculos(result.data);
setLoading(false);
}
useEffect(() => {
if (type === 1) {
getAllVeiculos();
}else if (type === 2) {
getOneVeiculos();
}
}, [type]);
return (
<div>
<button onClick={() => setType(2)}>Click</button>
{veiculos.map(item => (
<div>{item.name}</div>
))}
</div>
);
}

Related

TypeError: Cannot read properties of undefined (reading 'addEventListener'), don't know how to proceed

Learning react, currently trying to create a video call web app, however I get this error:
TypeError: Cannot read properties of undefined (reading 'addEventListener')
on this line of code:
useEffect(() => {
peer.addEventListener("negationneeded",handleNegotiation);
return () =>{
peer.removeEventListener("negotionneeded",handleNegotiation);
};
},[]);
handleNegotiation:
const handleNegotiation = useCallback(() => {
const localOffer = peer.localDescription;
socket.emit("call-user",{userID: remoteUserID, offe: localOffer });
}, []);
here is also the whole file:
import React, {useEffect, useCallback, useState} from 'react';
import ReactPlayer from "react-player";
import { useSocket} from "../providers/Socket";
import { usePeer } from "../providers/Peer";
const SessionPage = () => {
const { socket } = useSocket();
const { peer, createOffer, createAnswer,setRemoteAns,sendStream,remoteStream } = usePeer();
const [myStream,setMyStream] = useState(null);
const [remoteUserID, setRemoteUserID] = useState();
const handleNewUserJoined = useCallback(
async(data) =>{
const {userID} = data
console.log("New user joined the session",userID);
const offer = await createOffer();
socket.emit('call-user',{ userID, offer });
setRemoteUserID(userID);
},
[createOffer,socket]
);
const handleIncomingCall = useCallback( async(data) => {
const {from, offer} = data;
console.log("Incoming Call from", from, offer);
const ans = await createAnswer(offer);
socket.emit("call-accepted",{userID: from, ans});
setRemoteUserID(from);
},
[createAnswer, socket] );
const handleCallAccepted = useCallback(async(data) => {
const {ans} = data;
console.log("Call Got Accepted",ans);
await setRemoteAns(ans);
}, [setRemoteAns]);
const getUserMediaStream = useCallback(async() => {
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
setMyStream(stream);
}, []);
const handleNegotiation = useCallback(() => {
const localOffer = peer.localDescription;
socket.emit("call-user",{userID: remoteUserID, offe: localOffer });
}, []);
useEffect(() => {
socket.on("user-joined",handleNewUserJoined);
socket.on("incoming-call",handleIncomingCall);
socket.on("call-accepted",handleCallAccepted);
//return () =>{
// socket.off("user-joined",handleNewUserJoined);
//socket.off("incoming-call", handleIncomingCall);
//socket.off("call-accepted",handleCallAccepted);
//};
}, [handleCallAccepted,handleIncomingCall, handleNewUserJoined, socket]);
useEffect(() => {
peer.addEventListener("negationneeded",handleNegotiation);
return () =>{
peer.removeEventListener("negotionneeded",handleNegotiation);
};
},[]);
useEffect(() => {
getUserMediaStream();
},[]);
return(
<div className='session-page-container'>
<h1>Hi mom, Im on TV :D</h1>
<h4>You are now online with {remoteUserID}</h4>
<button onClick={(e) => sendStream(myStream)}>Share my video</button>
<ReactPlayer url={myStream} playing muted/>
<ReactPlayer url={remoteStream} playing/>
</div>
)
}
export default SessionPage;
--> Peer file
import React, { useMemo, useEffect, useState, useCallback } from "react";
const peerContext = React.createContext(null);
export const usePeer = () => React.createContext(null);
export const PeerProvider = (props) => {
const [remoteStream, setRemoteStream] = useState(null);
const peer = useMemo(() =>
new RTCPeerConnection({
iceServers: [
{
urls: [
"stun:stun.l.google.com:19302",
"stun:global.stun.twilio.com:3478",
],
},
],
}),
[]
);
const createOffer = async() => {
const offer = await peer.createOffer();
await peer.setLocalDescription(offer);
return offer;
};
const createAnswer = async (offer) => {
await peer.setRemoteDescription(offer);
const answer = await peer.createAnswer();
await peer.setLocalDescription(answer);
return answer;
};
const setRemoteAns = async(ans) =>{
await peer.setRemoteDescription(ans);
};
const sendStream = async(stream) => {
const tracks = stream.getTracks();
for(const track of tracks){
peer.addTrack(track,stream);
}
};
const handleTrackEvent = useCallback((ev) =>{
const streams = ev.streams;
setRemoteStream(streams[0]);
}, [])
useEffect(() => {
peer.addEventListener("track",handleTrackEvent);
return () =>{
peer.removeEventListener("track",handleTrackEvent)
}
},[handleTrackEvent, peer])
return(
<peerContext.Provider value={{ peer, createOffer, createAnswer, setRemoteAns, sendStream,remoteStream}}>{props.children}</peerContext.Provider>
);
};
useEffect with calling addEventListener on peer works earlier than some value assigned there.
Just add check of value existed:
useEffect(() => {
if (!peer) return
peer.addEventListener("track",handleTrackEvent);
return () =>{
peer.removeEventListener("track",handleTrackEvent)
}
},[handleTrackEvent, peer])

Uncaught TypeError: Cannot read properties of undefined (reading 'filter')

I am trying to connect frontend and backend. I can't understand why filter() function is undefined. In localhost it worked but when i am trying to put it on web with fly.io it doesn't work anymore. Here's code
import { useState, useEffect } from 'react'
import Person from './components/Person'
import Filter from './components/Filter'
import NewPersonForm from './components/NewPersonForm'
import personService from './services/persons'
import Notification from './components/Notification'
import './index.css'
const App = () => {
const [persons, setPersons] = useState([])
const [newName, setNewName] = useState('')
const [newNumber, setNewNumber] = useState('')
const [newFilter, setNewFilter] = useState('')
const [successMessage, setSuccessMessage] = useState(null)
useEffect(() => {
console.log('effect')
personService
.getAll()
.then(response => {
console.log('fulfilled')
setPersons(response.data)
})
}, [])
const addPerson = (event) => {
event.preventDefault()
const personObject = {
name: newName,
number: newNumber
}
console.log(newName)
const inArray = persons.filter((person) => person.name === newName)
if (inArray.length === 0){
personService
.create(personObject)
.then(response => {
setPersons(persons.concat(response.data))
setSuccessMessage(
`Added ${personObject.name}`
)
setTimeout(() => {
setSuccessMessage(null)
}, 3000)
setNewName('')
setNewNumber('')
})
console.log('button clicked', event.target)
} else{
alert(`${newName} is already in the phonebook`)
}
}
const deletePerson = (person) => {
const persontodelete = person.name
if (window.confirm(`Delete ${persontodelete}?`)){
personService
.remove(person.id)
.then(response => {
setPersons(persons.filter(p => p.id !== person.id))
setSuccessMessage(
`Deleted ${person.name}`
)
setTimeout(() => {
setSuccessMessage(null)
}, 3000)
})
}
}
const filteredPersons = persons.filter(person =>
person.name.toLowerCase().includes(newFilter.toLowerCase()))
const handlePersonChange = (event) => {
console.log(event.target.value)
setNewName(event.target.value)
}
const handleNumberChange = (event) => {
console.log(event.target.value)
setNewNumber(event.target.value)
}
const handleFilterChange = (event) => {
setNewFilter(event.target.value)
}
return (
<div>
<h2>Phonebook</h2>
<Notification message={successMessage} />
<Filter newFilter={newFilter} handleFilterChange={handleFilterChange}/>
<h2>add new</h2>
<NewPersonForm
addPerson={addPerson}
newName={newName}
handlePersonChange={handlePersonChange}
newNumber={newNumber}
handleNumberChange={handleNumberChange}
/>
<h2>Numbers</h2>
<Person filteredPersons={filteredPersons} deletePerson={deletePerson}/>
<div>debug: {newName}</div>
<div>debug: {newNumber}</div>
</div>
)
}
export default App
I am trying to get array with names and numbers from backend. Error disappears if I do following change
useEffect(() => {
console.log('effect')
personService
.getAll()
.then(data => {
console.log('fulfilled')
setPersons(data)
})
}, [])
but new error occures in toLowecase() function.
const filteredPersons = persons.filter(person =>
person.name.toLowerCase().includes(newFilter.toLowerCase()))

TypeError: Cannot read property 'map' of undefined React Hooks

I need some help understanding why I'm getting the error from the title: 'TypeError: Cannot read property 'map' of undefined'. I need to render on the page (e.g state & country here) some data from the API, but for some reason is not working.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const APIFetch = () => {
const [user, setUser] = useState('');
const [info, setInfo] = useState([]);
const fetchData = async () => {
const data = await axios.get('https://randomuser.me/api');
return JSON.stringify(data);
}
useEffect(() => {
fetchData().then((res) => {
setUser(res)
setInfo(res.results);
})
}, [])
const getName = user => {
const { state, country } = user;
return `${state} ${country}`
}
return (
<div>
{info.map((info, id) => {
return <div key={id}>{getName(info)}</div>
})}
</div>
)
}
Can you guys provide me some help? Thanks.
Try this approach,
const APIFetch = () => {
const [user, setUser] = useState("");
const [info, setInfo] = useState([]);
const fetchData = async () => {
const data = await axios.get("https://randomuser.me/api");
return data; <--- Heres is the first mistake
};
useEffect(() => {
fetchData().then((res) => {
setUser(res);
setInfo(res.data.results);
});
}, []);
const getName = (user) => {
const { state, country } = user.location; <--- Access location from the user
return `${state} ${country}`;
};
return (
<div>
{info.map((info, id) => {
return <div key={id}>{getName(info)}</div>;
})}
</div>
);
};
Return data without stringify inside the fetchData.
Access user.location inside getName.
Code base - https://codesandbox.io/s/sharp-hawking-6v858?file=/src/App.js
You do not need to JSON.stringify(data);
const fetchData = async () => {
const data = await axios.get('https://randomuser.me/api');
return data.data
}
Do it like that
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const APIFetch = () => {
const [user, setUser] = useState('');
const [info, setInfo] = useState([]);
useEffect(() => {
const fetchData = async () => {
const res = await axios.get('https://randomuser.me/api');
setUser(res.data);
setInfo(res.data.results);
}
featchData();
}, [])
const getName = user => {
const { state, country } = user;
return `${state} ${country}`
}
return (
<div>
{info.map((info, id) => {
return <div key={id}>{getName(info)}</div>
})}
</div>
)
}
Codesandbox: https://codesandbox.io/s/vigorous-lake-w52vj?file=/src/App.js

React Hooks state not updating variable in view

I'm trying to set a state, which I fetch from an API in the form of an array.
Tried this in every way possible, doesn't work.
Any ideas on how to fix this?
instance is an axios.create that creates the instance to a localhost django server which has CORS-ALLOW-CROSS-ORIGIN True
import React, { useState } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const response = await instance.get(`/one/list/`);
setOne(response.data);
};
fetchText();
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
Do it like this,
import React, { useState } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const response = await instance.get(`/one/list/`);
setOne(response.data);
};
useEffect(() => {
fetchText();
},[ any variable you want it to fetch that again ]);
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
This looks to be a good use case for a useEffect hook. Also, you need to await async functions within useEffect statement. The useEffect hook cannot be an async function in itself. Also, the original implementation would result in an infinite loop. The setState function would trigger a re-render which would then trigger the fetch function to fire off again. Consider the following:
import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const request= await instance.get(`/one/list/`);
request.then( r => setOne(r.data))
};
useEffect(() => {
(async () => {
await fetchText();
})();
}, []);
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
Based on you guys' advice, I came up with the below code which works fine so far.
import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const [el, setEl] = useState(null);
const fetchText = async () => {
let res = await instance.get("/one/list/");
setOne(res.data);
};
useEffect(() => {
(async () => {
await fetchText();
})();
}, []);
useEffect(() => {
const handleClick = e => {
let newEl = document.createElement("input");
newEl.value = e.target.innerHTML;
newEl.id = e.target.id;
newEl.addEventListener("keypress", e => handleInput(e));
newEl.addEventListener("focusout", e => handleInput(e));
e.target.parentNode.replaceChild(newEl, e.target);
newEl.focus();
};
const handleInput = async e => {
console.log(e.type);
if (
e.target.value !== "" &&
(e.key === "Enter" || e.type === "focusout")
) {
let payload = { text: e.target.value };
try {
e.preventDefault();
let res = await instance.put(`/one/update/${e.target.id}`, payload);
let text = res.data.text;
e.target.value = text;
let newEl = document.createElement("span");
newEl.innerHTML = text;
newEl.addEventListener("click", e => handleClick(e));
newEl.id = e.target.id;
e.target.parentNode.replaceChild(newEl, e.target);
} catch (e) {
console.error(e);
}
}
};
setEl(
one.map(o => (
<p>
<span id={o.id} onClick={e => handleClick(e)}>
{o.text}
</span>
</p>
))
);
}, [one]);
return <>{el}</>;
};
export default OneList;

Putting fetch function in a separate component

I'm trying to take out the fetchImages function from the following component and put it inside a new component:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import UnsplashImage from './UnsplashImage';
const Collage = () => {
const [images, setImages] = useState([]);
const [loaded, setIsLoaded] = useState(false);
const fetchImages = (count = 10) => {
const apiRoot = 'https://api.unsplash.com';
const accessKey =
'<API KEY>';
axios
.get(`${apiRoot}/photos/random?client_id=${accessKey}&count=${count}`)
.then(res => {
console.log(res);
setImages([...images, ...res.data]);
setIsLoaded(true);
});
};
useEffect(() => {
fetchImages();
}, []);
return (
<div className="image-grid">
{loaded
? images.map(image => (
<UnsplashImage
url={image.urls.regular}
key={image.id}
alt={image.description}
/>
))
: ''}
</div>
);
};
export default Collage;
For this, I created a new component called api.js, removed the entire fetchImage function from the above component and put it in to api.js like this:
api.js
const fetchImages = (count = 10) => {
const apiRoot = 'https://api.unsplash.com';
const accessKey =
'<API KEY>';
axios
.get(`${apiRoot}/photos/random?client_id=${accessKey}&count=${count}`)
.then(res => {
console.log(res);
setImages([...images, ...res.data]);
setIsLoaded(true);
});
};
export default fetchImages;
Next I took setIsLoaded(true); from api.js and paste it inside Collage component like this:
useEffect(() => {
fetchImages();
setIsLoaded(true);
}, []);
Now I can import fetchImages in to Collage component.
However, I don't know what should I do with this line inside the fetchImages function? This needs to go to Collage component, but res.data is not defined inside Collage component.
setImages([...images, ...res.data]);
How should I handle it?
There is many way to do that, but in your case.
You should use
const fetchImages = (afterComplete, count = 10) => {
const apiRoot = 'https://api.unsplash.com';
const accessKey = '<API KEY>';
axios
.get(`${apiRoot}/photos/random?client_id=${accessKey}&count=${count}`)
.then(res => {
console.log(res);
afterComplete(res.data);
});
};
export default fetchImages;
And in your Collage component:
const afterComplete = (resData) =>{
setImages([...images, ...resData]);
setIsLoaded(true);
}
useEffect(() => {
fetchImages(afterComplete);
}, []);
What you can do is create a custom hook ( sort of like a HOC)... Since I don't have an unsplash API key I'll give you an example with a different API but the idea is the same:
Here is your custom hook:
import { useState, useEffect } from 'react';
export const useFetch = url => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const fetchUser = async () => {
const response = await fetch(url);
const data = await response.json();
const [user] = data.results;
setData(user);
setLoading(false);
};
useEffect(() => {
fetchUser();
}, []);
return { data, loading };
};
Here is how you can use it in your component:
import { useFetch } from './api';
const App = () => {
const { data, loading } = useFetch('https://api.randomuser.me/');
return (
<div className="App">
{loading ? (
<div>Loading...</div>
) : (
<>
<div className="name">
{data.name.first} {data.name.last}
</div>
<img className="cropper" src={data.picture.large} alt="avatar" />
</>
)}
</div>
);
};
Here is a live demo: https://codesandbox.io/s/3ymnlq59xm

Resources