Home > Software design >  Map JSON if input field isnt currently 0
Map JSON if input field isnt currently 0

Time:03-20

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

enter image description here

Heres how I want it too look

enter image description here

And heres how it looks when you start searching

enter image description here

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.

  1. 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.

  2. The input still updates the query state

  3. I have a new function that filters the relevant data from the data state, and then maps out the values from the name properties.

  4. 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

  • Related