Ive just created a search and filtering input in React and have a bug where the JSON always maps the names of the suggest searches even if you dont search. I would like it so that the suggestions only appear if the user begins to type into the input field. I was wondering if this is possible by using an
if (variable.length != 0) {//Do something}
Heres my code
import * as React from 'react';
import { useState } from "react";
import { Link } from "react-router-dom";
const content = [
{link: '/review/elden-ring', name: 'Elden\nRing'},
{link: '/review/', name: 'defg'},
{link: '/review/', name: 'ghij'},
{link: '/review/', name: 'jklm'},]
export default function Search(props) {
//For storing and setting search input
const [query, setQuery] = useState("");
return (
//Search input
<div >
<form >
<input type="text" placeholder="Search" name="search" onChange={event => {setQuery(event.target.value)}}/>
<div > {/* Flex container to align the icon and bar */}
<button type="submit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> {/* ! Font Awesome Pro 6.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
</button>
</div>
</form>
{/* Search Suggestions */}
<div >
{content.filter((content) => {
//If input return object
if (query == "") {
return content
}
//If any input characters much object characters return corresponding object
else if (content.name.toLowerCase().includes(query.toLocaleLowerCase())) {
return content
}
})
//Maps element based on the number of json objects
.map((content) => {
return(
<div >
<Link to={content.link}><p>{content.name}</p></Link>
</div>
);
})};
</div>
</div>
);
};
Heres how the input currently looks
Heres how I want it too look
And heres how it looks when you start searching
CodePudding user response:
For your particular case, you're having results even when user has not typed anything, so you could useMemo
hook to calculate results that you'll be showing (This can help you with performance in a future too)
const MyComponent = () => {
const [query, setQuery] = useState('');
const results = useMemo(() => {
if (!query) {
return [];
}
return data.filter(item => item.name.toLowercase().includes(query.toLowercase()));
}, [query])
return <div onFocus={onFocus}>{results}</div>
}
Old approach (onFocus):
You can add a onFocus
function that updates your state
const MyComponent = () => {
const [data, setData] = useState([]);
const onFocus = () => {
const data = getNewData();
setData(data);
}
return <div onFocus={onFocus}>{data}</div>
}
CodePudding user response:
It's often useful to separate out functions to make things a little easier to debug.
I don't know where your data is coming from so in this example I'm passing it into the component as a prop. But I'm also immediately storing it in state.
The input still updates the
query
stateI have a new function that
filters
the relevant data from thedata
state, and thenmaps
out the values from the name properties.Finally, if the length of the filtered data is 0 show "No results", otherwise, call the
createJSX
function with the filtered data.
const { useState } = React;
function Example({ arr }) {
// So: I like to store data in state. At some
// point I might want to switch to a different state
// management system like Context, or Recoil, or Redux.
// In this case I'm just passing in the data as a property
// of the component, and then setting the data state with it
const [data, setData] = useState(arr);
const [query, setQuery] = useState('');
// This does what your code already does - it
// sets the query state to the value of the input
// See: destructuring assignment in the footnotes
function handleChange(e) {
const { value } = e.target;
setQuery(value);
}
// Here we're filtering out the data (just like your
// code), and then `mapping` over the returned objects
// to get an array of names (just like your code)
function getFiltered(data) {
return data.filter(obj => {
const name = obj.name.toLowerCase();
return name.includes(query.toLowerCase());
}).map(obj => obj.name);
}
// Just creating some elements
function createJSX(arr) {
return arr.map(el => <li key={el}>{el}</li>);
}
// Getting the filtered elements
const filtered = getFiltered(data);
// And now we're using a ternary operator
// to either display "No results" if there's nothing in
// the filtered array, or the result of calling
// `createJSX` with the filtered results
return (
<div>
<input
type="text"
placeholder="Search"
onChange={handleChange}
/>
{filtered.length
? <ul>{createJSX(filtered)}</ul>
: <div>No results</div>
}
</div>
);
}
const arr = [{name: 'abcd'}, {name: 'defg'}, {name: 'ghij'}, {name: 'jklm'}];
ReactDOM.render(
<Example arr={arr} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation