React+Next.js: cannot read properties of undefined when using onHandleDelete - reactjs

function DetailContainer() {
const router = useRouter();
const { id } = router.query;
const numbering = Number(id);
const currentPostList = useRecoilValue(PostListState);
const [postObj] = currentPostList.filter((post) => post.id === numbering);
const title = postObj.title;
const content = postObj.content;
const date = postObj.date;
const postId = postObj.id;
const setPostList = useSetRecoilState(PostListState);
const onHandleDelete = (postId) => {
setPostList(currentPostList.filter((item) => item.id !== postId));
};
return (
<>
<div>{title}</div>
<div>{content}</div>
<div>{date}</div>
<Link href="/EditorPage">
<button>Edit</button>
</Link>
<button>Remove</button>
</>
);
}
When I do not add onHandleDelete to Remove button, it works fine. But when I add onHandleDelete, error 'cannot read properties of undefined' appears. Is there a way that I can fix this so that I can delete postObj?
=> I fixed error by replacing to 'postObj?.title' since postObj has been removed when button was clicked

What is postId? When you are filtering "currentPostList" it check for the all items whose ids are not equal to postId. Call onHandleDelete on Remove button and pass postId when calling.
Something like this:
<button onClick={() => handleDelete("paste postId here if you are getting it from props")>Remove</button>
it will work.

Related

Error message "Cannot read properties of null (reading 'filter')"

I'm new to learning react and have been having problems getting the array to filter using the .filter() method. I'm trying to create a grocery list and I keep getting the error message "Cannot read properties of null (reading 'filter')" Can someone please assist me on getting this work? Here is the code that I have.
import Header from './Header';
import SearchItem from './SearchItem';
import AddItem from './AddItem';
import Content from './Content';
import Footer from './Footer';
import { useState, useEffect } from 'react';
function App() {
const [items, setItems] = useState(JSON.parse(localStorage.getItem('shoppinglist')));
const [newItem, setNewItem] = useState('')
const [search, setSearch] = useState('')
console.log('before useEffect')
//useEffect looks to it's dependency and if the dependency changes then it will run the anonymous function
useEffect(() => {
console.log('inside useEffect')
},[items])
const setAndSaveItems = (newItems) => {
setItems(newItems);
localStorage.setItem('shoppinglist', JSON.stringify(newItems));
}
console.log('after useEffect')
const addItem = (item) => {
const id = items.length ? items[items.length - 1].id + 1 : 1;
const myNewItem = { id, checked: false, item };
const listItems = [...items, myNewItem];
setAndSaveItems(listItems);
}
const handleCheck = (id) => {
const listItems = items.map((item) => item.id === id ? { ...item, checked: !item.checked } : item);
setAndSaveItems(listItems);
}
const handleDelete = (id) => {
const listItems = items.filter((item) => item.id !== id);
setAndSaveItems(listItems);
}
const handleSubmit = (e) => {
e.preventDefault();
if (!newItem) return;
addItem(newItem);
setNewItem('');
}
return (
<div className="App">
<Header title="Grocery List" />
<AddItem
newItem={newItem}
setNewItem={setNewItem}
handleSubmit={handleSubmit}
/>
<SearchItem
search={search}
setSearch={setSearch}
/>
<Content
items={items.filter(item => ((item.item).toLowerCase()).includes(search.toLowerCase()))}
handleCheck={handleCheck}
handleDelete={handleDelete}
/>
<Footer length={items.length} />
</div>
);
}
export default App;
I feel that you're mentioning about this code excerpt:
items.filter((item) => item.id !== id);
can you please check if the items array is null or not. Only if items is null, filtering wouldn't be applicable and you will receive such error messages
can you log items before deletion?
Few pointers that could help
initilize the items in an useEffect as it could be null, it will make it easy to fetch data a api later
const [items, setItems] = useState([]);
useEffect(() => {
try {
const items = JSON.parse(localStorage.getItem('shoppinglist'))
setItems(items)
} catch(error) {
}
}, [])
// put ?. checks on items when calling filter, map
const handleDelete = (id) => {
const listItems = items?.filter((item) => item.id !== id);
if (listItems) {
setAndSaveItems(listItems);
}
}
Id generated will clash and cause bugs
const id = items.length ? items[items.length - 1].id + 1 : 1;
if the person deletes on item and adds another the new item will have the same id as the last one
item { id: 1}
item { id: 2}
item { id: 3}
after deleting id 2, when you add new items it will have id 3
and will cause bugs with select
either use a id that is a timestamp or check for unique ids
Save the items in local storage on submit, as calls get/set items to localstorage can lead to performace issues in the UI
Checkout the new docs on working with arrays
Hope it helps

When Url Typed I need to fetch the single data in react js while calling api

enter image description here
When I typed this http://localhost:3000/hell/:2 the page loads but will not fetch a single item that has id 2
But When I clicked the button then the page shows the single item that has id 2
I need to show the data when I entered http://localhost:3000/hell/:2 as URL
""""""""""CODE Gets RUN but it was showing in a paragraph format so I had edit and made code easier to understand """""""""
the code is --->>
At App.js-->
<div className="App">
<div className='body'>
<Router history={History}>
<Switch>
<Route path="/hell/:id"><Hell/></Route>
</Switch>
</Router>
</div>
</div>
At Hello.js-->
let {id} = useParams();
let di = id;
const [loading,setloading] = useState([false]);
const [posts,setposts] = useState([]);
const [search,setsearch] = useState("");
const [message,setmessage] = useState("");
const history = useHistory();
useEffect(()=>{
const getload = async ()=>{
setloading(true);
const response = await axios.get(`http://127.0.0.1:8000/list/`);
const message = "Error";
setposts(response.data);
setmessage(message);
setloading(false);
}
},[]);
console.log({di});
function inputhandler(a){
id = a;
history.push(generatePath("/hell/:id", { id }));
setsearch(a);
}
return (
<div>
<h1>Find : {id}</h1>
{
posts.map((curElem) =>{
return(
<div>
<Link key={curElem.id} to={curElem.id} onClick={() => inputhandler(curElem.id)}>{curElem.title}</Link>
</div>
)
})
}
{
loading ?(<h4>Loading...{message}</h4>):(
(posts.filter((value)=>{
if(value.id===(search)){
return message;
}
})
.map(item =><Help key={item.id} title={id}></Help>)))
}
</div>
)
}
You've accessed the id route match param but then never used it.
You do not form the link targets correctly. Once you are linking correctly then there is no need for the extraneous onClick handler to set any search state with the item.id value since you can consume the linked id right from the params.
Remember to also call getload so the posts state is updated.
Hello.js
const history = useHistory();
const { id } = useParams(); // <-- current `id` from "/hell/:id"
const [loading,setloading] = useState([false]);
const [posts,setposts] = useState([]);
const [message,setmessage] = useState("");
useEffect(()=>{
const getload = async ()=>{
setloading(true);
const response = await axios.get(`http://127.0.0.1:8000/list/`);
const message = "Error";
setposts(response.data);
setmessage(message);
setloading(false);
}
getload(); // <-- need to call to make the GET request
},[]);
return (
<div>
<h1>Find : {id}</h1>
{posts.map((curElem) => {
return(
<div key={curElem.id}> // <-- React key on outermost element
<Link
// generate target path here
to={generatePath("/hell/:id", { id: curElem.id })}
>
{curElem.title}
</Link>
</div>
)
})}
{loading
? <h4>Loading...{message}</h4>
: posts.filter((value) => value.id === id) // <-- filter by id
.map(item => <Help key={item.id} title={id}></Help>)
}
</div>
)

useState method not updating state with onClick?

I am creating a custom multiple choice question, but I am having difficulties updating my selection choice using useState.
const QuestionPage = ({ audioFiles }) => {
const [choice, setChoice] = useState(-1); // -1 is when none of the choices are selected
const updateChoice = val => {
setChoice(val);
}
return (
// each MultipleChoice contains an audio file and a radio button
<MultipleChoice audioFiles={audioFiles} choice={choice} updateChoice={updateChoice} />
)
};
const MultipleChoice = ({ audioFiles, choice, updateChoice }) => {
const answerOption = audioFiles.map((item, key) =>
<AudioButton file={file} index={key} choice={choice} updateChoice={updateChoice} />
);
return (
{answerOption}
);
}
const AudioButton = ({ file, index, choice, updateChoice }) => {
const handleClick = (val) => {
updateChoice(val);
};
const radioButton = (
<div className={`${index === choice ? "selected" : ""}`} onClick={() => handleClick(index)}>
</div>
);
return (
<>
{radioButton}
<Audio file={file} />
</>
);
}
In the first function, QuestionPage within updateChoice, when I use console.log(val), it updates according to the selections I make (i.e. 0 and 1). However, when I call console.log(choice), it keeps printing -1.
In addition, I keep getting an error message that says updateChoice is not a function.
Any advice? Thanks in advance!
Looks like you did not pass the value of audioFiles in MultipleChoice function

React, unexpected multiple result when using map and fetch

I am building Weather App, my idea is to save city name in localStorage, pass a prop to child component, then iterate using map and display each in seperate child of the first child
The problem is that displayed data doubles/triples on render(depending on component when render occurs) so when I have for example city London and add city Berlin it will render:
London,London,Berlin
The problem is not in AddCity component, it's working correctly but in this mix of asynchronous setState/fetching and maping
Please see the code below
App(parent component)
const App = () => {
const [cities, setCities] = useState([]);
const addCity = (newCity)=>{
console.log('adding')
setCities([...cities, newCity]);
let cityId = localStorage.length;
localStorage.setItem(`city${cityId}`, newCity);
}
useEffect(() => {
loadCityFromLocalStore()
}, [])
const loadCityFromLocalStore =()=>{
setCities([...cities, ...Object.values(localStorage)])
}
return (
<div>
<Header />
<AddCity addCity={addCity}/>
<DisplayWeather displayWeather={cities}/>
</div>
)
}
DisplayWeather (first child)
const DisplayWeather = ({displayWeather}) => {
const apiKey = '4c97ef52cb86a6fa1cff027ac4a37671';
const [fetchData, setFetchData] = useState([]);
useEffect(() => {
displayWeather.map(async city=>{
const res =await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=>[...fetchData , data]));
})
}, [displayWeather])
return (
<>
{fetchData.map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
}
Weather component
const Weather = ({data}) => {
return (
<li>
{data.name}
</li>
)
}
It looks like the problem comes from calling setFetchData for cities that you already added previously.
One easy way to fix it would be to store fetch data as an object instead of a dictionary so that you just override the data for the city in case it already exists (or maybe even skip the fetch as you already have the data).
For example:
const [fetchData, setFetchData] = useState({});
useEffect(() => {
displayWeather.map(async city=>{
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
})
}, [displayWeather])
Then, to map over fetch data you can just use Object.values:
return (
<>
{Object.values(fetchData).map(data=>(
<ul>
<Weather
data={data}/>
</ul>
))}
</>
)
If you want to skip already fetched cities you can do something like this instead:
useEffect(() => {
displayWeather.map(async city=>{
if (!fetchData[city]) {
const res = await fetch(`http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`)
const data = await res.json();
setFetchData((fetchData=> ({...fetchData, [city]: data})));
}
})

React Hooks - Remove Specific Item from an array using Index

I have two arrays,
const [imagesUrlArray, setURls] = useState([])
const [imagesArray, setImages] = useState([])
using handle change below; imagesUrlArray is used to display the images on the screen, and imagesArray is saved to later update those same images to the database
const handleChange = (e) => {
let selected = e.target.files[0]
var selectedImageSrc = URL.createObjectURL(selected);
addUrl(selectedImageSrc)
addImage(selected)
};
Though I now want to click the X(delete) button and remove the item at index of imagesUrlArray and imagesArray (say if the user no longer wants to use that image)
<div className="img-grid">
{ imagesUrlArray && imagesUrlArray.map((url,index) => {
return ( <div key={index}
className="img-wrap">
{/* <span class="close">×</span> */}
<button onClick={ () => handleRemove(index)} className="close">X</button>
<img src={url} alt="uploaded" />
</div>
)
}
)}
</div>
I have tried splice and slice etc but still cannot find a perfect solution,
here is the handleRemove Function
const handleRemove = (index) => {
const newImagesArray = imagesArray.splice(index);
const newImagesUrlArray = imagesUrlArray.splice(index);
setImages(newImagesArray);
setURls(newImagesUrlArray);
}
You can do something like this:
const handleRemove = (index) => {
setImages(imagesArray.filter((x,i) => i !== index));
setURls(imagesUrlArray.filter((x,i) => i !== index));
}
So, basically the idea is to skip the element at specific index and return the remaining items and set the state.

Resources