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

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])

Related

TypeError: "function" is not a function

Currently trying to learn React and I am making a video calling web app. The whole purpose of the web app is to simply enter in a session together with another person and share your video and audio. However I am encountering the following issue:
TypeError: sendStream is not a function
Apparently the function sendStream is not a function, that function is assigned to a button and the way it should work is that everytime we click on "Share my video" the video and the audio of the other person who is also in the same session should be shared.
Here they are:
sendStream
const sendStream = async(stream) => {
const tracks = stream.getTracks();
for(const track of tracks){
peer.addTrack(track,stream);
}
};
where it is used
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>
)
The entire components
** File Peer.jsx (where the function sendStream is created**
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(() => {
if (!peer) return
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>
);
};
File Session.jsx where it is used
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("incomming-call",handleIncomingCall);
socket.on("call-accepted",handleCallAccepted);
//return () =>{
// socket.off("user-joined",handleNewUserJoined);
//socket.off("incomming-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
Can anyone please help me out and make sure that this work the way it should work?
You are creating two different contexts here:
const peerContext = React.createContext(null);
export const usePeer = () => React.createContext(null);
It should be:
const peerContext = React.createContext(null);
export const usePeer = () => useContext(peerContext);

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()))

React Hook error in array handing in useEffect() and setState does not set the state

I'm having two problems in this code. The first is in the second useEffect. For the reason that I don't understand the useEffect stops working every now and then and causes an error "Cannot read property 'toLowerCase'". Removing the toLowerCase does not solve the problem, but the whole array handling seems to be impossible at that time.
The other problem is in the function addName. setNewName does not set newName. That one I've tried in various kinds of forms, such as setNewName(...newName, {name: '', number: ''}), setNewName('') inside .then and else as well as outside else.
...
import React, {useState, useEffect} from 'react'
import Filter from './components/Filter'
import PersonForm from './components/PersonForm'
import Persons from './components/Persons'
import personService from './services/person'
const App = () => {
const [person, setPerson] = useState([])
const [newName, setNewName] = useState({name: '', number: ''})
const [filteredPerson, setFilteredPerson] = useState([''])
const [searchTerm, setSearchTerm] = useState('')
useEffect(() => {
personService
.getAll()
.then(initialPersons => {
setPerson(initialPersons)
})
}, [])
useEffect( () => {
const results = person.filter( p =>
p.name.toLowerCase().includes(searchTerm) )
setFilteredPerson(results)
},[person,filteredPerson] )
const addName = (event) => {
event.preventDefault()
const nameObject = {
name: newName.name,
number: newName.number
}
if (person.some(p => p.name === newName.name)
) {
window.alert(`${newName.name} is already added to phonebook`)
}
else {
personService
.create(nameObject)
.then(returnedPerson => {
setPerson(person.concat(returnedPerson))
setNewName({name: '', number: ''})
})
console.log('newName', newName.name )
}
}
const handleAddPerson = (event) => {
console.log('event.target.name ', event.target.name)
console.log('event.target.value ', event.target.value)
setNewName({...newName,
[event.target.name]: event.target.value
})
}
const handleSearchTerm = (event) => {
setSearchTerm(event.target.value)
}
return (
<div >
<h2>Phonebook</h2>
<Filter searchTerm={searchTerm} onChange={handleSearchTerm} />
<h3>Add a new</h3>
<PersonForm onSubmit={addName} onChange={handleAddPerson} />
<h2>Numbers</h2>
<Persons list={filteredPerson} />
</div>
);
}
export default App;
...
import axios from 'axios'
const baseUrl = 'http://localhost:3001/persons'
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
const create = newObject => {
const request = axios.post(baseUrl, newObject)
return request.then(response => response.data)
}
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject)
return request.then(response => response.data)
}
/*const updater = {
getAll,
create,
update
}*/
export default {
getAll,
create,
update
}
EDIT
Use async await in your personService so you can return response instead of return request.then(...) something like:
const getAll = async () => {
const response = await axios.get(baseUrl);
return response;
}
After that you can do as follows in your useEffect
useEffect(() => {
(async () => {
const response = await personService.getAll();
if (response.status === 200) {
setPerson(response.data);
const filtered = response.data.filter(item =>
item.name.toLowerCase().includes(searchTerm)
);
setFilteredPerson([...filtered]);
}
})();
}, []);

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

Set an empty array in state

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>
);
}

Resources