How to properly search in a list in ReactJS

Viewed 8579

I am trying to set a simple search operation in a user interface as shown below:

search

I have a total of 70 react-strap cards and each card contain a vessel with name, type and an image. I would like to search the name of the vessel and have the card related to that vessel to pop-up. All my images are currently contained inside the external database Contentful. Below the fields of interests:

The problem is that I don't know how to write a search function that locate a specific value of a list.

list

Below the code:

SideBar.js

import React from 'react';
import Client from '../Contentful';
import SearchVessel from '../components/SearchVessel';

class Sidebar extends React.Component {
    state = {
        ships: [],
    };

    async componentDidMount() {
        let response = await Client.getEntries({
            content_type: 'cards'
        });
        const ships = response.items.map((item) => {
            const {
                name,
                slug,
                type
            } = item.fields;
            return {
                name,
                slug,
                type
            };
        });

        this.setState({
            ships
        });
    }

    getFilteredShips = () => {
        if (!this.props.activeShip) {
            return this.state.ships;
        }
        let targetShip = this.state.ships.filter(
            (ship) => this.props.activeShip.name === ship.name
        );
        let otherShipsArray = this.state.ships.filter((ship) => this.props.activeShip.name !== ship.name);
        return targetShip.concat(otherShipsArray);
    };

    render() {
        return (
            <div className="map-sidebar">
                {this.props.activeShipTypes}

                <SearchVessel />

                <pre>
                    {this.getFilteredShips().map((ship) => {
                        console.log(ship);
                        return (
                            <Card className="mb-2">
                                <CardImg />
                                <CardBody>
                                    <div className="row">
                                        <img
                                            className="image-sizing-primary"
                                            src={ship.companylogo.fields.file.url}
                                            alt="shipImage"
                                        />
                                    </div>
                                    <div>
                                        <img
                                            className="image-sizing-secondary"
                                            src={ship.images.fields.file.url}
                                            alt="shipImage"
                                        />
                                    </div>
                                    <CardTitle>
                                        <h3 className="thick">{ship.name}</h3>
                                    </CardTitle>
                                    <CardSubtitle>{ship.type}</CardSubtitle>
                                    <CardText>
                                        <br />
                                        <h6>Project Details</h6>
                                        <p>For a description of the project view the specification included</p>
                                    </CardText>
                                    <Row style={{ marginTop: '20px' }}>
                                        <div className="buttoncontainer">
                                            <div className="btn btn-cards">
                                                <a
                                                    className="buttonLink"
                                                    download
                                                    href={ship.projectnotes.fields.file.url}
                                                >
                                                    Project Notes
                                                </a>
                                            </div>
                                            <div className="btn btn-cards">
                                                <a className="buttonLink" href={ship.abstract.fields.file.url}>
                                                    Abstract
                                                </a>
                                            </div>
                                        </div>
                                    </Row>
                                </CardBody>
                            </Card>
                        );
                    })}
                </pre>
            </div>
        );
    }
}

export default Sidebar;

VesselSearch.js

import React, { Component } from 'react';

export default class SearchVessel extends Component {
    render() {
        const { value, handleSubmit, handleChange } = this.props;

        return (
            <React.Fragment>
                <div className="container">
                    <div className="row">
                        <div className="col-10 mx-auto col-md-8 mt-5 text-center">
                            <h4 className="text-slanted text-capitalize">Search for Vessel</h4>
                            <form className="mt-4" onSubmit={handleSubmit}>
                                <label htmlFor="search" className="text-capitalize">
                                    type vessel separated by comma
                                </label>
                                <div className="input-group">
                                    <input
                                        type="text"
                                        name="search"
                                        placeholder="Type name of vessel here"
                                        className="form-control"
                                        value={value}
                                        onChange={handleChange}
                                    />
                                    <div className="input-group-append">
                                        <button type="submit" className="input-group-text bg-primary text-white">
                                            <i className="fas fa-search" />
                                        </button>
                                    </div>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>
            </React.Fragment>
        );
    }
}

What I have done so far:

1) I tried different combination with the filter function and I think I am close. The problem is that when I operate the search nothing happens and in order to find the card of the vessel I want, I have to scroll down until I find it.

I am running out of ideas and if you see something I didn't catch point me in the right direction for solving this issue.

2 Answers

You're close! I would add a field to your state called 'searchText' and then create a method to filter based on that searchText state item.

getFilteredShips = () => this.state.ships.filter(s => s.name.includes(this.state.searchText)

Then just map over those values to render the cards that match the search text. The cards will update each time the searchText value updates.

this.getFilteredShips().map(ship => ..........

React is famous for re-usable component. You will have all the data of these vessels in an array. You will loop through the array and render the items with card component.And when you search for the specific card you want that vessel to pop out on top.

There are two ways to do it:

You have to run through the array, find the index of that vessel and do whatever it takes to manipulate your array and to make that item at top and re-render your list.

Alternatively render one more component on top of your vessel list as user clicks the search button. You just have to find the item index and render it. This way you don't have to deal with array manipulation. It doesn't matter if you have 80 or 1000 cards.

Please checkout official documentation for array methods, for array slicing and splice. Hope this is what you are looking for. If you need further help, comment please.

Related