Joey Smith
Joey Smith

Reputation: 35

What is the best way to render a search bar component that takes in a props in another component using react v6

I have my search component that is supposed to be passed into a header component but the tutorial I am using is old and was using an older version of react.

This is my Search.js component.

import React, { useState } from 'react'
import { useParams, useNavigate } from 'react-router-dom'

const Search = ({ history }) => {

    const [keyword, setKeyword] = useState('');

    const searchHandler = (e) => {
        e.preventDefault()

        if (keyword.trim()) {
            history.push(`/search/${keyword}`)
        } else {
            history.push('/')
        }
    }

    console.log('my keyword', keyword);

    return (
            <form onSubmit={searchHandler}>
                 <div className="input-group">
                    <input
                        type="text"
                        id="search_field"
                        className="form-control"
                        placeholder="Enter Product Name ..."
                        onChange={(e) => setKeyword(e.target.value)}
                    />
                    <div className="input-group-append">
                        <button id="search_btn" className="btn">
                        <i className="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
        </div>
            </form>       
    )
}

export default Search

And this is how I pass it into my Header.js component

import React, { Fragment } from 'react'
import { Route, Link } from 'react-router-dom'

import { useDispatch, useSelector } from 'react-redux'
import { useAlert } from 'react-alert'
import { logout } from '../../actions/userActions'

import Search from './Search'

import '../../App.css'

const Header = () => {
    
    
    return (
        <Fragment>
            <nav className="navbar row">
                <div className="col-12 col-md-6 mt-2 mt-md-0">
                
                    <Route render={({ history }) => <Search history={history} />} />
                    
                </div>
             </nav>
        </Fragment>
    )
}

export default Header

Whenever I run the code it tells me I need to wrap it in a <Routes>

I did it like this

<Routes> 
  <Route render={({ history }) => <Search history={history} />} />
</Routes>

But when I wrap it like this it doesn't give any errors but the search bar doesn't show at all in my browser header

What is the correct way to render this please?

I also read that the history.push in my search.js has been replaced by a useNavigate so I tried changing that to make my Search.js look like this

import React, { useState } from 'react'
import { useParams, useNavigate } from 'react-router-dom'

const Search = ({  }) => {
   
    const [keyword, setKeyword] = useState('');
    const navigate = useNavigate();

    const searchHandler = (e) => {
        e.preventDefault()

        if (keyword.trim()) {
            navigate(`/search/${keyword}`)
        } else {
            navigate('/search')
        }
    }

    console.log('my keyword', keyword);

    return (
            <form onSubmit={searchHandler}>
                 <div className="input-group">
                    <input
                        type="text"
                        id="search_field"
                        className="form-control"
                        placeholder="Enter Product Name ..."
                        onChange={(e) => setKeyword(e.target.value)}
                    />
                    <div className="input-group-append">
                        <button id="search_btn" className="btn">
                        <i className="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
        </div>
            </form>       
    )
}

export default Search

But I have no idea how to render it properly in my Header component

Upvotes: 3

Views: 2191

Answers (1)

Drew Reese
Drew Reese

Reputation: 203257

From what I can tell you are using react-router-dom v6, so your last snippet of the Search component using the useNavigate hook is correct.

const Search = () => {
  const [keyword, setKeyword] = useState("");
  const navigate = useNavigate();

  const searchHandler = (e) => {
    e.preventDefault();

    if (keyword.trim()) {
      navigate(`/search/${keyword}`);
    } else {
      navigate("/search");
    }
  };

  console.log("my keyword", keyword);

  return (
    <form onSubmit={searchHandler}>
      <div className="input-group">
        <input
          type="text"
          id="search_field"
          className="form-control"
          placeholder="Enter Product Name ..."
          onChange={(e) => setKeyword(e.target.value)}
        />
        <div className="input-group-append">
          <button id="search_btn" className="btn">
            <i className="fa fa-search" aria-hidden="true"></i>
          </button>
        </div>
      </div>
    </form>
  );
};

But the issue is that you are trying to render it on a Route using the older RRDv5 Route APIs.

<Route render={({ history }) => <Search history={history} />} />

In v6 gone are the component, and render and children function props. Gone also are the route props, i.e. history, location, and match, this is why you need the useNavigate hook to get the navigate function in Search.

From what I can tell, Search was only rendered by a Route in the old tutorial simply to have the history object passed in as a prop. I don't see any reason for it to be rendered by a Route. Just render Search directly in the Header.

const Header = () => {
  return (
    <nav className="navbar row">
      <div className="col-12 col-md-6 mt-2 mt-md-0">
        <Search />
      </div>
    </nav>
  );
};

Assuming the Header component is rendered within a routing context provided by a router (BrowserRouter, HashRouter, MemoryRouter, etc...) the useNavigate hook will work.

Upvotes: 2

Related Questions