Please help me! Delete Icon is not functional, when I click on delete icon it delete all the contact, on refreshing, it returns all the previous contacts. I am also using localStorage.
I have added all the Component of the React App Project.
App.js
import { v4 as uuid } from "uuid";
const App = () => {
const LOCAL_STORAGE_KEY = "contacts";
const [contacts, setContacts] = useState([]);
const addContactHandler = (contact) => {
console.log(contact);
setContacts([...contacts, { id: uuid(), ...contact }]);
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
};
useEffect(() => {
const retrieveContacts = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY)
);
if (retrieveContacts) {
setContacts(retrieveContacts);
}
}, []);
useEffect(() => {
if (contacts.length) {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}
}, [contacts]);
return (
<>
<div className="app">
<Header />
<AddContact addContactHandler={addContactHandler} />
<ContactList contacts={contacts} getContactId={removeContactHandler} />
</div>
</>
);
};
export default App;
ContactList.js
const ContactList = (props) => {
const deleteContactHandler = (id) => {
props.getContactId(id);
};
const renderContactList = props.contacts.map((contact) => {
return (
<>
<ContactCard
contact={contact}
clickHandler={deleteContactHandler}
key={contact.id}
/>
</>
);
});
return (
<>
<div className="contactList">
<h2 className="contactList__title">Contact List</h2>
<div className="contactList__container">
{renderContactList}
</div>
</div>
</>
);
};
ContactCard.js
const ContactCard = (props) => {
const { id, name, email } = props.contact;
return (
<>
<div className="contactCard">
<div className="contactCard__contact">
<img
className="contactCard__userIcon"
src={userIcon}
alt="user-icon"
/>
<div className="contactCard__userName">
<h2>{name}</h2>
<p>{email}</p>
</div>
</div>
<div className="contactCard__delIcon">
<img
src={delIcon}
alt="del-icon"
onClick={() => props.clickHandler(id)}
/>
</div>
</div>
</>
);
};
export default ContactCard;
I have researched out the references. Unable to get the Solution.
The effect to store the contacts do not save empty arrays.
Thats why you get the old array after refreshing your page.
Just remove the condition.
useEffect(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(contacts));
}, [contacts]);
But you should consider to remove this effect.
Save the contacts directly after setting the state instead.
const addContactHandler = (contact) => {
console.log(contact);
const newContactList = [...contacts, { id: uuid(), ...contact }];
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
const removeContactHandler = (id) => {
const newContactList = contacts.filter((contact) => {
return contact.id !== id;
});
setContacts(newContactList);
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newContactList));
};
Related
I was creating the functionality of pinning and unpinning of particular note, so when the user clicks the thumbtack icon I want that icon of only that particular note changes to a cross icon but when I am clicking on the second notes to pin it then the icon that changed on previous pinned note gets restored to its original form.
I have created the pinning functionality using onPin function but struggling with changing the icon of that particular pinned item.
I want to add icons to pinned items in such a way that previously added close icons stay in their place and do not get updated.
What I tried?
So i created the state variable iconId which is an array so whenever the user clicks pinned icon then new id will be pushed to the iconId array and while displaying the output I put the condition that if the current id is included in iconId array then change icon of all those respective ids in iconId to cross icon, apparently this functionality dint work.
-----------------------App.js--------------------------------
import React, { useState } from "react";
import './App.css';
import Input from './Components/Input';
import Navbar from './Components/Navbar';
import Notesview from './Components/Notesview';
import Notesdata from "./Data/Notesdata";
function App() {
const [data, setData] = useState(Notesdata);
// const [pin, setpin] = useState(true)
const [iconId, seticonId] = useState([])
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id)
setData(newData)
console.log(newData)
console.log(Notesdata)
console.log(0)
}
function handlePost(value) {
// Notesdata.push(value)
// setData(Notesdata)
// // console.log(typeof data)
// console.log(Notesdata)
setData([...data, value]);
}
function onPin(id) {
let index = data.map((item) => {
return item.id
}).indexOf(id)
let arr1 = data.slice(0, index).concat(data.slice(index + 1))
arr1.unshift(data[index])
setData(arr1);
seticonId([...iconId] , id)
console.log(iconId)
}
function handleclose() {
// setpin(!pin)
// seticonId("")
}
return (
<div className="App">
<header className="App-header">
<Navbar />
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview handleDelete={handleDelete} Data={data} onPin={onPin} iconId={iconId} handleclose={handleclose} />
</header>
</div>
);
}
export default App;
----------------Noteview function(mapping function)---------------
import React from 'react'
import Notescard from './Notescard'
import "../Styles/Notes.css"
// import { useState } from 'react'
const Notesview = ({ Data, handleDelete, onPin , iconId, handleclose}) => {
return (
<>
<div className='notes'>
{Data && Data.map((item) => {
return <Notescard item={item} handleDelete={handleDelete} onPin={onPin} iconId={iconId} key={item.id} handleclose={handleclose}/>
})
}
</div>
</>
)
}
export default Notesview
-----------------------------Notescard component------------------
import React from "react";
import "../Styles/Notescard.css";
import { FaThumbtack, FaTrashAlt, FaPencilAlt ,FaTimesCircle} from "react-icons/fa";
// import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Notescard = ({ item , handleDelete,onPin,iconId,handleclose, key}) => {
return (
<>
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? <FaTimesCircle onClick={handleclose}/> : <FaThumbtack id={item.id} onClick={() => onPin(item.id)}/> }
<FaTrashAlt onClick={() => handleDelete(item.id)}/>
<FaPencilAlt />
</div>
</div>
</>
);
};
export default Notescard;
Issue
You are passing two arguments to the seticonId state updater function.
seticonId([...iconId], id)
The id is never added to the iconId state.
Solution
Use a functional state update to append the id to the array.
seticonId((iconId) => iconId.concat(id));
Code:
const Notescard = ({ item, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="box">
<div className="content">
<h2 className="item1">{item.title}</h2>
<h4 className="item1"> {item.tagline}</h4>
<p className="item2">{item.description}</p>
</div>
<div className="icons">
{iconId.includes(item.id) ? (
<FaTimesCircle onClick={() => handleclose(item.id)} />
) : (
<FaThumbtack id={item.id} onClick={() => onPin(item.id)} />
)}
<FaTrashAlt onClick={() => handleDelete(item.id)} />
<FaPencilAlt />
</div>
</div>
);
};
...
const Notesview = ({ Data, handleDelete, onPin, iconId, handleclose }) => {
return (
<div className="notes">
{Data.map((item) => {
return (
<Notescard
item={item}
handleDelete={handleDelete}
onPin={onPin}
iconId={iconId}
key={item.id}
handleclose={handleclose}
/>
);
})}
</div>
);
};
...
export default function App() {
const [data, setData] = useState(Notesdata);
const [iconId, seticonId] = useState([]);
function handleDelete(id) {
let newData = data.filter((item) => item.id !== id);
setData(newData);
console.log(newData);
console.log(Notesdata);
console.log(0);
}
function handlePost(value) {
setData([...data, value]);
}
function onPin(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.unshift(data[index]);
return arr1;
});
seticonId((iconId) => iconId.concat(id));
}
function handleclose(id) {
setData((data) => {
const index = data.findIndex((item) => item.id === id);
const insertIndex = data.findIndex((item) => !iconId.includes(item.id));
const arr1 = data.slice(0, index).concat(data.slice(index + 1));
arr1.splice(insertIndex - 1, 0, data[index]);
return arr1;
});
seticonId((iconId) => iconId.filter((elId) => elId !== id));
}
return (
<div className="App">
<Input data={data} handlePost={(value) => handlePost(value)} />
<Notesview
handleDelete={handleDelete}
Data={data}
onPin={onPin}
iconId={iconId}
handleclose={handleclose}
/>
</div>
);
}
Edit
I fixed it by adding permissions during token generation:
permissions: ['allow_join', 'allow_mod'],
Reproducible demo here
Whenever I try to toggle the mic, webcam or screenshare system, I keep receiving the error:
TypeError: this._mediasoupDevice is null
I've looked everywhere online, I wasn't able to get a single relevant result.
Looking into the source code, the program attempts to to call canProduce on _mediasoupDevice, which obviously fails. I'm not sure why this is happening. Is something wrong with my code or is it something else?
Here's my whole relevant file (backend routes excluded, but you can see them in the above source code):
import * as React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { MeetingProvider, MeetingConsumer, useMeeting, useParticipant } from '#videosdk.live/react-sdk';
import { startLiveStream } from 'src/features/streaming/redux/actions';
import { IState } from 'src/features/core/redux/reducers';
// Helper function for participant loop.
const chunk = (arr) => {
const newArr = [];
while (arr.length) newArr.push(arr.splice(0, 3));
return newArr;
};
const ParticipantView = ({ participantId }) => {
const webcamRef = React.useRef(null);
const micRef = React.useRef(null);
const screenShareRef = React.useRef(null);
const { displayName, webcamStream, micStream, screenShareStream, webcamOn, micOn, screenShareOn } =
useParticipant(participantId);
React.useEffect(() => {
if (webcamRef.current) {
if (webcamOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(webcamStream.track);
webcamRef.current.srcObject = mediaStream;
webcamRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
webcamRef.current.srcObject = null;
}
}
}, [webcamStream, webcamOn]);
React.useEffect(() => {
if (micRef.current) {
if (micOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(micStream.track);
micRef.current.srcObject = mediaStream;
micRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
micRef.current.srcObject = null;
}
}
}, [micStream, micOn]);
React.useEffect(() => {
if (screenShareRef.current) {
if (screenShareOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(screenShareStream.track);
screenShareRef.current.srcObject = mediaStream;
screenShareRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
screenShareRef.current.srcObject = null;
}
}
}, [screenShareStream, screenShareOn]);
return (
<div key={participantId}>
<audio ref={micRef} autoPlay />
{webcamRef || micOn ? (
<div>
<h2>{displayName}</h2>
<video height={'100%'} width={'100%'} ref={webcamRef} autoPlay />
</div>
) : null}
{screenShareOn ? (
<div>
<h2>Screen Shared</h2>
<video height={'100%'} width={'100%'} ref={screenShareRef} autoPlay />
</div>
) : null}
<br />
<span>
Mic:{micOn ? 'Yes' : 'No'}, Camera: {webcamOn ? 'Yes' : 'No'}, Screen Share:{' '}
{screenShareOn ? 'Yes' : 'No'}
</span>
</div>
);
};
const MeetingView = React.memo(() => {
const [joined, setJoined] = React.useState(false);
const { join, leave, toggleMic, toggleWebcam, toggleScreenShare, participants } = useMeeting();
if (!joined) {
return (
<button
onClick={() => {
join();
setJoined(true);
}}
>
join meeting
</button>
);
}
return (
<div>
<div>
<button onClick={leave}>Leave</button>
<button onClick={toggleMic}>toggleMic</button>
<button onClick={toggleWebcam}>toggleWebcam</button>
<button onClick={toggleScreenShare}>toggleScreenShare</button>
</div>
{chunk([...participants.keys()]).map((k) => (
<div key={k} style={{ display: 'flex' }}>
{k.map((l) => (
<ParticipantView key={l} participantId={l} />
))}
</div>
))}
</div>
);
});
const Landing = React.memo(() => {
const [loading, setLoading] = React.useState(false);
const meetingId = useSelector((state: IState) => state.app.streaming.streamConfig.meetingId);
const videoToken = useSelector((state: IState) => state.app.streaming.streamConfig.videoToken);
const dispatch = useDispatch();
const handleClick = React.useCallback(() => {
dispatch(startLiveStream({ payload: { onStart: () => setLoading(true), onEnd: () => setLoading(false) } }));
}, []);
if (!meetingId || !videoToken)
return (
<div>
<button onClick={handleClick}>Go Live</button>
{loading && <div>LOADING</div>}
</div>
);
return (
<MeetingProvider
config={{
meetingId,
name: '<Name-of-participant>',
// participantId: 'Id-of-participant', // optional, auto-generated
micEnabled: true,
webcamEnabled: true,
// maxResolution: '<Maximum-resolution>',
}}
token={videoToken}
>
<MeetingConsumer>{() => <MeetingView />}</MeetingConsumer>
</MeetingProvider>
);
});
export default Landing;
P.S. I'm aware my public and secret keys are exposed here -- new keys will be generated whenever I deploy.
Also, if it matters, I'm using Chrome, but the same issue transpires in Firefox.
So I was trying to update the value I got by the Addlist and I tried this but this isn;t working. Also when I click on the '+' button without writing anything, an empty list is created. How should I stop it. I've attached a code below.
import React from "react";
import "./App.css";
import { useState } from "react";
import TodoList from "./components/TodoList";
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const updateList = (e) => {
setInput(e.target.value);
};
const AddList = () => {
console.log("value added")
setList((addValue) => {
return [...addValue, input];
});
setInput("");
};
const updateItems=(id)=>{
const newValue=[...list].map((newVal)=>{
if(input.id===id){
input.text='';
}
return newVal;
})
setList(newValue);
}
const deleteItems = (id) => {
console.log("deleted");
setList((addValue) => {
return addValue.filter((element, index) => {
return index !== id;
});
});
};
return (
<div className="todo-app">
<h1> Enter Anything</h1>
<input
type="text"
placeholder="Add anything"
value={input}
onChange={updateList}
/>
<button onClick={AddList}>+</button>
<ul>
{list.map((itemsvalue, id) => {
return (
<TodoList
itemsValue={itemsvalue}
key={id}
onSelect={deleteItems}
id={id}
onUpdate={updateItems}
/>
);
})}
</ul>
</div>
);
}
export default App;
Any kind of help would be appreciated. Also if I want to split this into multiple components is there a way to do.
When user clicks on the add button there is the check for empty String AddList method
for ex:- User updates second index value, second position value will get updated.
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [index, setIndex] = useState(null);
const updateList = (e) => {
setInput(e.target.value);
};
useEffect(() => {
setList(list);
console.log(list, '<>?');
}, [index]);
const AddList = () => {
if (input.trim() !== '') {
setList([...list, input]);
}
setInput('');
};
const updateValue = (index) => {
console.log(list[index]);
setIndex(index);
if (list[index].trim() !== '') {
setInput(list[index]);
}
};
const UpdateList = () => {
list[index] = input;
console.log(list, 'before <>?');
setIndex(null);
setInput('');
};
return (
<div>
<input type="text" placeholder="Add anything" value={input} onChange={updateList} />
<button disabled={!index && !list.length === 0} onClick={AddList}>
Add
</button>
<button disabled={input.trim() === ''} onClick={UpdateList}>
Update
</button>
{list.map((m, index) => (
<h1 style={{ border: '1px solid black' }} onClick={() => updateValue(index)}>
{m}
</h1>
))}
</div>
);
I have one button in front each list item when I click on the watched button, I want the text of the button to be changed to not watched(or when click on not watched it change to watched) and to be included in the watched list. At the top, I have three buttons, watched and not watched , which one I clicked on. Show me the list of the movies that I changed, their state and for third button(with text of all )it shows the whole list.I think that my problem is handleWatchedBtn function . this is picture of project maybe it is simple my explanation! thank you for your help.
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setMovie(
response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}))
);
setLoading(false);
});
}, []);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
setfilterByWatch({ filterByWatch: filter });
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
function filterItems() {
return Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filterItems().map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
You could introduce a new array state which stores all filtered movies and is updated every time the movies or the filter are updated.
You can then pass its reference to the map function that generates the list.
Also, notice that I've added the spread operator (...) in the handleWatchedBtn function and adjusted the handleWatchedChange method to update the state to a string and not an object.
Try to change your code like this:
import React, { useEffect, useState } from "react";
const App = () => {
const [Movies, setMovie] = useState([]);
const [filteredMovies, setFilteredMovies] = useState([]);
const [Loading, setLoading] = useState(true);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
const newMovies = response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}));
setMovie(newMovies);
setLoading(false);
});
}, []);
useEffect(() => {
// Update filtered movies when the data or the filter changes
const newFilteredMovies = Movies.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
setFilteredMovies(newFilteredMovies);
}, [Movies, filterByWatch]);
function handleWatchedBtn(id) {
setMovie(() =>
Movies.map((movie) => {
if (movie.id === id) {
// Add the spread operator here
return { ...movie, watched: !movie.watched };
}
return movie;
})
);
}
function handleWatchedChange(filter) {
// Change this line
setfilterByWatch(filter);
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input type="text" value={Keyword} onChange={handleKeywordChange} />
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filteredMovies.map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const getRandomMusic = () => {
return fetchMusic().then((result) => result.data);
};
const Button = (props) => {
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
<SomeComponent />
<p>Some text here</p>
</div>
);
};
musicList is an array of objects having name and year .
const musicList = [{
name: 'abc',
year :1990
},
{
name:'xyz',
year:1989'
}
]
I want to save the data returned from getRandomMusic function and pass it to SomeComponent component.
You could use useState (doc)
const Button = props => {
const [musicList, setMusicList] = React.useState([])
const getRandomMusic = () => {
fetchMusic().then(result => {
setMusicList(result.data)
})
}
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
<SomeComponent musicList={musicList} />
<p>Some text here</p>
</div>
)
}