I am trying to create a search feature using react hooks but it keeps returning the error:
Cannot read Property of Undefined
on the updateSearch function whenever I type in the input field.
const [search, setSearch] = React.useState('');
const [searchResults, setSearchResults] = React.useState([]);
const [state, setState] = React.useState({
open: false,
name: '',
users:[]
});
useEffect(() => {
getAllUsers();
}, []);
const getAllUsers = () => {
fetch('/userinformation/', {
method: 'GET',
headers: {'Content-Type':'application/json'}
})
.then(function(response) {
return response.json()
}).then(function(body) {
console.log(body);
setState({...state, users: body });
})
}
const updateSearch = (event) => {
setSearch(event.target.value)
}
React.useEffect(() => {
const results = state.users.filter(user =>
user.toLowerCase().includes(search)
);
setSearchResults(results);
}, [search]);
return (
<input type="text" value={search} onChange={(e) => updateSearch(e.target.value)}/>
)
Whenever I type in the search bar I get the following error:
How can i fix this?
You can either get to the value of passed event by changing
<input type="text" value={search} onChange={(event) => updateSearch(event}/>
or you can keep the input element as it is and change the update updateSearch callback to
const updateSearch = (event) => { setSearch(event) }
Secondly, you are applying includes to a single item of an array which is specific to array methods, you need to make following changes as well to make it work:
React.useEffect(() => {
const results = state.users.filter( user => user.firstName.toLowerCase() === search );
setSearchResults(results);
}, [search])
in your input you're already passing valueonChange={(e) => updateSearch(e.target.value) and in updateSearch you're trying to accessing it. Change it like this, if you want to access event in updateSearch method and get value from it.
<input type="text" value={search} onChange={(e) => updateSearch(e}/>
I would teach you a secret that has worked very well for me over the years. When javascript gives you such error as cannot read property ***whateverValue*** value of undefined it means you are trying to read the property of an object that does not exist. In this case, the object you're trying to read from is undefined, hence it cannot have any key: value pair.
Back to your question: TypeError: Cannot read property value of undefined
Using cmd+f to check for all places where value is used shows me everywhere you used value on event.target.value
Stay with me (I know this is boring, but it would help later).
You have an event handler named updateSearch.
All you need here now is to change your input tag to this:
<input type="text" value={search} onChange={updateSearch}/>
Don't worry, React would handle the rest, it automatically parses the event to eventHandler which can then be accessed on such eventHandler.
Also, I think you might want to refactor this component.
Something like import React, {useState, useEffect} from React
you won't have to call React.useEffect or React.useState in other parts of the project. just useEffect or useState.
Goodluck :+1
You have already passed the value of the input into the updateSearch method.
This is how you should fix it:
const updateSearch = (value) => {
setSearch(value);
};
And as for the second issue you have raised on your useEffect hook, you will have to call toLowerCase() on one of your properties (either firstName or lastName), depending on what you need to search for.
React.useEffect(() => {
const results = state.users.filter(user =>
user.firstName.toLowerCase().includes(search)
);
setSearchResults(results);
}, [search]);
Related
I'm getting this error as soon as I input a second character:
Uncaught TypeError: Cannot create property 'value' on string 'a'
The code so far:
function App() {
const [object, setObject] = useState({
name: "",
value: "",
});
const handleChange = (e) => {
setObject((object) => (object.value = e.target.value));
console.log(object);
};
return (
<div>
<input type="text" onChange={handleChange} placeholder="Type something" />
</div>
);
}
This error occurs because the setState function is not being handled properly here.
Unlike the setState method found in class components, useState does not automatically merge update objects.
So we can say that the real question is:
How to change a single property of a state object using useState hook?
Solution 1: You can return a new object by spreading over previous state.
const handleChange = (e) => {
setObject((prevObject) => ({ ...prevObject, value: e.target.value }));
console.log(object);
};
Solution 2: You can create a new state object and completely replace the old one.
In this solution, you cannot guarantee that object has been refreshed to the latest version.
Also, even if it were, you're setting object to be the same object reference it currently holds, therefore it will not be recognised as a change and the value will not be set.
const handleChange = (e) => {
const temp = object;
temp["value"] = e.target.value;
setObject(temp);
console.log(object);
};
See these questions:
Updating and merging state object using React useState() hook
Cannot create property on string
I am building a simple blog app and I am trying to update title of the blog But it is not updating, it is just showing the current state.
I have tried many times by changing the method of setting state but it is still showing that error.
App.js
function BlogDetail() {
const [blogName, setBlogName] = useState("");
axios.get("/api/blog_detail/70/").then(res => {
setBlogName(res.data[0].blog_name)
})
const handleChange = (e) => {
console.log(e.target.value)
setBlogName({
...blogName,
[e.target.name]: e.target.value
})
}
const saveBlog = (e) => {
// sending to API
console.log(blogName)
}
return (
<div>
<form>
{blogName}
<input type="text" name="blogName" value={blogName} onChange={e => handleChange} />
<button type="submit" onClick={e => saveBlog(e)}>Save</button>
<form>
</div>
)
}
And When I update on change instead of updating on submit
onChange=(e => setBlogName(e.target.value))
Then it is showing
A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined
I have tried many times but it is still not working.
input requires a string as a value, but you are trying to pass an object:
setBlogName({
...blogName,
[e.target.name]: e.target.value
})
instead pass a string:
setBlogName(e.target.value)
Also, you need to execute handleChange function and pass the event param.
onChange={e => handleChange(e)}
Edit:
Looked at it second time and it should be like this:
function BlogDetail() {
const [blogName, setBlogName] = useState("");
// without this you override state every time you press a key
useEffect(() => {
axios.get("/api/blog_detail/70/").then(res => {
setBlogName(res.data[0].blog_name)
})
}, [])
const handleChange = (e) => {
// just use value here
setBlogName(e.target.value)
}
const saveBlog = (e) => {
// sending to API
console.log(blogName)
}
return (
<div>
<form>
{blogName}
{ /* remember to run the function */ }
<input type="text" name="blogName" value={blogName} onChange={e => handleChange()} />
<button type="submit" onClick={e => saveBlog(e)}>Save</button>
<form>
</div>
)
}
Besides the problem that within handleChange you need to pass an an string value to setBlogName you also need to wrap your axios fetch call in a useEffect.
The problem is that everytime you trigger a rerender while calling setBlogName you are calling your API point again and set the value back to the fetched value.
You should prevent that by doing the following ->
useEffect(() => {
axios.get("/api/blog_detail/70/").then(res => {
setBlogName(res.data[0].blog_name)
}), [])
Don't forget to install { useEffect } from 'react'.
And well of course update handleChange ->
const handleChange = (e) => {
const newBlogPostName = e.target.value
console.log(newBlogPostName)
setBlogName(newBlogPostName)
}
you have not any action in this method. where is the update state?
const saveBlog = (e) => {
// sending to API
console.log(blogName)
}
and in this method you change the string to an object
const handleChange = (e) => {
console.log(e.target.value)
setBlogName({
...blogName,
[e.target.name]: e.target.value
})
}
so the problem is that your function updates your state to an object and then you want to display that object(not a string property of that object) in the DOM. its wrong because you cant display objects in the DOM in react. in this case, you even get an error because you cant use spread operator on strings. you cant do something like this: ...("test")
const handleChange = (e) => {
console.log(e.target.value)
//the state value will be an object. its wrong. you even get an error
because of using spread operator on a string
setBlogName({
...blogName //this is a string at the init time,
[e.target.name]: e.target.value
})
}
so whats the solution?
you should update your state to a string or use a string property of the object.
something like this:
const handleChange = (e) => {
console.log(e.target.value)
setBlogName("string")
}
return (<>{blogName}</>)
thats it.
I want to get a list of values updated whenever its value is changed through a hook setState function, however I am getting an error I don't know why... I am getting a .map is not a function TypeError
Down bellow is my code and I also have a codesandbox link: https://codesandbox.io/s/fervent-satoshi-zjbpg?file=/src/Incomes.js:23-1551
import axios from "axios";
import { useState, useEffect } from "react";
const fetchInvestment = async () => {
const res = await axios.get(
"https://6r3yk.sse.codesandbox.io/api/investments/60b2696de8be014bac79a2a1"
);
return res.data.invest.incomes;
};
export default function Incomes() {
const [incomes, setIncomes] = useState([]);
const [date, setDate] = useState(undefined);
const [value, setValue] = useState(undefined);
useEffect(() => {
const getInvestments = async () => {
const res = await fetchInvestment();
setIncomes(res);
};
if (incomes.length === 0) {
getInvestments();
}
console.log(incomes);
}, [incomes]);
return (
<div>
<h1>Hello CodeSandbox</h1>
<input
id="monthEl"
type="month"
value={date}
onChange={(e) => {
setDate(e.target.value);
}}
/>
<input
id="monthEl"
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button
onClick={() => {
const income = {};
income[date] = Number(value);
setIncomes(incomes.push(income));
setTimeout(() => {
console.log(incomes);
}, 2000);
}}
>
Save
</button>
<ul>
{incomes.map((income) => (
<li key={Object.keys(income)}>
{Object.keys(income)}: {Object.values(income)}
</li>
))}
</ul>
</div>
);
}
Replace this line:
setIncomes(incomes.push(income));
With this
setIncomes([...incomes, income]);
The .push method returns the length of the array, not the actual array. You can use the spread operator to spread the current array and then add on the new item to the end of it.
Doing this should also work:
incomes.push(incomes)
setIncomes(incomes)
It's possible you're getting that error because the data coming back from your API isn't an array. Judging by your code, I'm guessing you're expecting a key/value map, which in JS is an object. You might be able to use Object.keys(incomes).map(...), but without knowing the specific response format, I can't say for sure.
There are 2 other issues with your code:
First, you can't push onto incomes because it's a React state array. Instead, you need to use the setIncomes callback...
setIncomes([...incomes, income])
Additionally, the way you're using Object.keys and Object.values is not correct. Again, can't say what the correct way is without knowing specifics of your response format.
I verushc an array from one component to another component.
The initial array is filled by a DB and is not empty.
If I try to map over the array in my second component, it is empty (length = 0);
However, after I wrote a value in a search box to filter the array, all articles appear as intended.
What is that about?
export default function Einkäufe({ alleEinkäufe, ladeAlleEinkäufe, url }) {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchResults, setSearchResults] = React.useState(alleEinkäufe);
const listeFiltern = (event) => {
setSearchTerm(event.target.value);
};
React.useEffect(() => {
setSearchResults(alleEinkäufe);
}, []);
React.useEffect(() => {
const results = alleEinkäufe.filter((eink) =>
eink.artikel.toLowerCase().includes(searchTerm.toLowerCase())
);
setSearchResults(results);
}, [searchTerm]);
[...]
{searchResults.map((artikel, index) => {
return ( ... );
})}
}
The problem is with your useEffect hook that sets the list of searchResults, it's not rerun when alleEinkäufe property is updated. You need to add alleEinkäufe as it's dependency.
React.useEffect(() => {
setSearchResults(alleEinkäufe);
}, [alleEinkäufe]);
My bet is that the parent component that renders Einkäufe is initially passing an empty array which is used as searchResults state and then never updated since useEffect with empty dependencies array is only run once on the component's mount.
I would also advise you to use English variable and function names, especially when you ask for assistance because it helps others to help you.
Your search term intially is "". All effects run when your components mount, including the effect which runs a filter. Initially, it's going to try to match any article to "".
You should include a condition to run your filter.
React.useEffect(() => {
if (searchTerm) {
const results = alleEinkäufe.filter((eink) =>
eink.artikel.toLowerCase().includes(searchTerm.toLowerCase())
);
setSearchResults(results);
}
}, [searchTerm]);
BTW, "" is falsy.
I am attempting to use react-widgets with onSearch with Multiselect. I can see that onSearch gets called with searchTerm. But, the response never becomes part of the options/data for Multiselect.
I've tried returning the results in an Array or in an object with the key data to the Array.
const getOptions = searchTerm => {
return [{ _id: 1, username: 'Bob' }];
}
const TagItem = ({ username }) => <span>{username}</span>
const MultiSelectUsers = ({ input, ...rest }) =>
<Multiselect {...input}
onBlur={() => input.onBlur()}
value={input.value || []}
valueField="_id"
tagComponent={TagItem}
itemComponent={TagItem}
onSearch={getOptions}
{...rest}
/>
Bonus: How do I use onSearch with Promise?
A couple of things need to happen here.
1) onSearch merely calls a callback, ie, it updates nothing. The update needs to be done via data. (Eg, use state in MultiSelectUsers)
2) The trick is that #1 won't work, unless you set filter={false} in Multiselect