'Uncaught TypeError: Cannot read property 'value' of null in ReactJS
I am trying to create a search bar in react that searches through TMDB API but I'm having some issues. Whenever I type into the search bar I get an error saying that Cannot read properties of null (reading 'value') and I'm not sure what the issue could be and why its returning as null. I call the mention in
function Search() {
const [searchQuery, setSearchQuery] = useState("");
const [timeoutId, updateTimeoutId] = useState();
const fetchData = async (searchString) => {
const response = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=f134dfeac1ebb17feefa58d7f94e94cd&language=en-US&query=${searchString}&page=1&include_adult=false`);
console.log(response);
};
const onTextChange = (e) => {
clearTimeout(timeoutId);
setSearchQuery(e.target.value);
const timeout = setTimeout(() => fetchData(e.target.value), 500);
updateTimeoutId(timeout);
};
return(
<div>
<SearchBox>
<SearchInput placeholder="SEARCH" value={searchQuery} onChange={onTextChange}></SearchInput>
</SearchBox></div>
);
}
This is the searchInput code
const SearchInput = styled.input`
color: black;
font-size: 16px;
font-weight: bold;
border: none;
outline: none;
margin-left: 15px;
`;
Solution 1:[1]
You could try with optional chaining (?.)
function Search() {
const [searchQuery, setSearchQuery] = useState("");
const [timeoutId, updateTimeoutId] = useState();
const fetchData = async (searchString) => {
const response = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=f134dfeac1ebb17feefa58d7f94e94cd&language=en-US&query=${searchString}&page=1&include_adult=false`);
console.log(response);
};
const onTextChange = (e) => {
clearTimeout(timeoutId);
setSearchQuery(e.target?.value);
const timeout = setTimeout(() => fetchData(e.target?.value), 500);
updateTimeoutId(timeout);
};
return(
<div>
<SearchBox>
<SearchInput placeholder="SEARCH" value={searchQuery} onChange={onTextChange}></SearchInput>
</SearchBox></div>
);
}
Solution 2:[2]
e.target.value does not exist anymore once the timeout has come back from the web api. The function has ended its execution and the e argument won't exist anymore.
Instead, try useRef instead of e.target.value in your setTimeout callback function, so when it comes back, it can reference the latest changes.
Something like this:
function Search() {
const [searchQuery, setSearchQuery] = useState("");
const [timeoutId, updateTimeoutId] = useState();
const query = useRef(searchQuery);
const fetchData = async (searchString) => {
const response = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=f134dfeac1ebb17feefa58d7f94e94cd&language=en-US&query=${searchString}&page=1&include_adult=false`);
console.log(response);
};
const onTextChange = (e) => {
clearTimeout(timeoutId);
setSearchQuery(e.target.value);
query.current = e.target.value;
const timeout = setTimeout(() => fetchData(query.current), 500);
updateTimeoutId(timeout);
};
return(
<div>
<SearchBox>
<SearchInput placeholder="SEARCH" value={searchQuery} onChange={onTextChange}></SearchInput>
</SearchBox></div>
);
Solution 3:[3]
After taking another look at your code I suspect it's the second event access in the timeout callback that's since been nullified. You should save a reference to the event's value within the callback scope so there isn't a dependency on the event object persisting.
const onTextChange = (e) => {
const { value } = e.target;
clearTimeout(timeoutId);
setSearchQuery(value);
const timeout = setTimeout(() => fetchData(value), 500);
updateTimeoutId(timeout);
};
Also, FYI, unless you need the timeoutId as part of state to trigger a rerender or something, it's more common to use a React ref here to store a reference to the timer id. From what I can tell you are simply throttling the fetchData call and there is no need to trigger unnecessary renders.
Example:
const timeoutIdRef = useRef();
useEffect(() => {
// clear any running timeouts upon component unmount
return () => clearTimeout(timeoutIdRef.current);
}, []);
...
const onTextChange = (e) => {
const { value } = e.target;
clearTimeout(timeoutIdRef.current);
setSearchQuery(value);
timeoutIdRef.current = setTimeout(() => fetchData(value), 500);
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Zach Jensz |
| Solution 2 | |
| Solution 3 | Drew Reese |
