Search code examples
javascriptreactjsreact-bootstrap

Generating Search suggestions in React?


I am looking to generate search suggestions that match data collected, like so:

enter image description here

As you type in you get suggestions: enter image description here

I am referencing some of the tutorial work from WesBos:

https://github.com/wesbos/JavaScript30/blob/master/06%20-%20Type%20Ahead/index-FINISHED.html

I've got the data logging in the console but now I am unsure how to get it to render. Below are my components (My thoughts were to generate the divs as a loop in App.js and pass the props to Match.js which I would eventually import but I am not sure if I am approaching this wrong):

App.js

import React, { Component } from 'react';
import { Form, Button } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';


const my_data = require('./data/test.json')

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      links: [],
      selectedLink:null,
      userLocation: {},
      searchInput: "",
      showMatches: false,
      matches: []
    };
  }

  componentDidMount() {
    fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
      .then(res=> res.json())
      .then(res=> 
          //console.log(json)
          this.setState({links:res})
      );
  }

  render() {

    const handleInputChange = (event) => {
      event.preventDefault()
      this.setState({searchInput: event.target.value })
      //console.log(event.target.value)
    }

    const handleSubmit = (event) => {
      event.preventDefault()
      const data = this.state
      displayMatches();
    }

    const findMatches = (wordToMatch, my_obj) => {
      return my_obj.filter(place => {
        // here we need to figure out the matches
        const regex = new RegExp(wordToMatch, 'gi');
        //console.log(place.street_address.match(regex))
        return place.street_address.match(regex)
      });
    }

    const displayMatches =() => {
      const matchArray = findMatches(this.state.searchInput, this.state.links);
      matchArray.map(place => {
        console.log(place.street_address);
        this.setState({matches:place})
        this.setState({showMatches:true})
      });
    }

    return (
      <div>
         <Form style = {{width: "75%"}} onSubmit = {handleSubmit}>
            <Form.Group controlId="formSearch">
              <Form.Control 
              type="text" 
              name = "my_search" 
              placeholder="Search for a Link Near you..." 
              onChange = {handleInputChange} />
                </Form.Group>
                  <Button variant="primary" type="submit">
                    Search
                  </Button>
          </Form>
          <div>
            {`How can I generate the console logged values as dynammic suggestions?`}
          </div>

      </div>
    );
  }
}

export default App;

Match.js

import React from 'react';

const match = ( props ) => {

    return (
        <div className="Matches">
            <p>{`data is passed: ${props.address}`}</p>
        </div>
    )
};

export default match;

Appreciate the help.

Answers - Using Suggestions below

App.js

import React, { Component } from 'react';
import { Form, Button, ListGroup } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import Match from './Match'

const my_data = require('./data/test.json')

class App extends Component {

  state = {
    links: [],
    selectedLink:null,
    userLocation: {},
    searchInput: "",
    showMatches: false,
    matches: [],
    searchLink:[]
}

componentDidMount() {
    fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
        .then(res=> res.json())
        .then(res=> 
            //console.log(json)
            this.setState({links:res})
        );
}

handleInputChange = (event) => {
    event.preventDefault()
    this.setState({searchInput: event.target.value })
    console.log(event.target.value)
}

handleSubmit = (event) => {
    event.preventDefault()
    this.displayMatches();
}

findMatches = (wordToMatch, my_obj) => {
    return my_obj.filter(place => {
        // here we need to figure out the matches
        const regex = new RegExp(wordToMatch, 'gi');
        //console.log(place.street_address.match(regex))
        return place.street_address.match(regex)
    });
}

displayMatches =() => {
    const matchArray = this.findMatches(this.state.searchInput, this.state.links);
    const newStateMatches = matchArray.map(place => {
        console.log(place.street_address);
        return place   
    });
    this.setState({matches:newStateMatches})
    this.setState({showMatches:true})
}

alertClicked =(event) => {
  //alert('you clicked an item in the group')
  const data = event.target
  console.log('clicked this data:', data)
  this.setState({searchLink: event.target})
  console.log(this.state.searchLink)
}

render() {
    return (
        <div>
            <input 
                placeholder="Search for a Link Near you..." 
                onChange = {this.handleInputChange} 
                value = {this.state.searchInput}
            />
            <Button onClick={this.handleSubmit}>
                Search
            </Button>
            <ListGroup defaultActiveKey="#link1">
              {
                this.state.matches.map(match => {
                  return <Match 
                            address={match.street_address} 
                            alertClicked={this.alertClicked}/>
                })
              }
            </ListGroup>

        </div>
    );
}
}

export default App;

Match.js

import React from 'react';
import { ListGroup } from 'react-bootstrap';

const match = ( props ) => {

    return (
        <ListGroup.Item 
            className="Matches" 
            action onClick={props.alertClicked}>
              <p>{`${props.address}`}</p>
        </ListGroup.Item>

    )
};

export default match;

Solution

  • First move all your method definitions outside of your render function (you'll need to update const and add this.

    in your display matches you should be building a newstate array then setState with the new array once built

    i do not use react bootstrap but it did not appear that your submit button was within the form therefor was not submitting the form.

    Make sure react components are capitalized (match component should be Match)

    I passed the whole 'place' down to the Match component via place prop:

    <Match place={place} />
    

    if you want to access the address like you did you would need to pass each individual value from the place down to the Match component like:

    <Match address={place.address} />
    

    (also if you are only initializing state before first render you can do so outside of the constructor)

    I simplified the return statement to just use a plain input and button tag for simplicity but you can probably get going from here

    Working Snippet:

    const Match = ( props ) => {
    
        return (
            <div className="Matches">
                <p>{`data is passed: ${props.place.street_address}`}</p>
            </div>
        )
    };
    
    class SomeComponent extends React.Component{
        state = {
            links: [],
            selectedLink:null,
            userLocation: {},
            searchInput: "",
            showMatches: false,
            matches: []
        }
        
        componentDidMount() {
            fetch('https://data.cityofnewyork.us/resource/s4kf-3yrf.json')
                .then(res=> res.json())
                .then(res=> 
                    //console.log(json)
                    this.setState({links:res})
                );
        }
        
        handleInputChange = (event) => {
            event.preventDefault()
            this.setState({searchInput: event.target.value })
            //console.log(event.target.value)
        }
        
        handleSubmit = (event) => {
            event.preventDefault()
            this.displayMatches();
        }
        
        findMatches = (wordToMatch, my_obj) => {
            return my_obj.filter(place => {
                // here we need to figure out the matches
                const regex = new RegExp(wordToMatch, 'gi');
                //console.log(place.street_address.match(regex))
                return place.street_address.match(regex)
            });
        }
        
        displayMatches =() => {
            const matchArray = this.findMatches(this.state.searchInput, this.state.links);
            const newStateMatches = matchArray.map(place => {
                console.log(place.street_address);
                return place   
            });
            this.setState({matches:newStateMatches})
            this.setState({showMatches:true})
        }
        
        render() {
            return (
                <div>
                    <input 
                        placeholder="Search for a Link Near you..." 
                        onChange = {this.handleInputChange} 
                        value = {this.state.searchInput}
                    />
                    <button onClick={this.handleSubmit}>
                        Search
                    </button>
                    {this.state.matches.map((place)=>{
                      return <Match place={place} />
                    })}
            
                </div>
            );
        }
    }
    
    ReactDOM.render(
    <SomeComponent />,
    document.getElementById("react")
    );
    <div id='react'></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>