3

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.

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • Are you getting anything in your console.log(ship) statement? – LHM Mar 06 '20 at 19:34
  • @LHM, thanks for reading the question :) . Basically I get all the ships from the console log. However the search (or filter does not work properly. I made [this](https://www.dropbox.com/s/fc5v5olecfumdmg/output.mp4?dl=0) if that could be useful to better understand my final goal. – Emanuele Mar 06 '20 at 19:51
  • Basically I have a total of 80 cards, each card has a name, vessel type and image. I would like to search (or filter) the name of the vessel. – Emanuele Mar 06 '20 at 19:53
  • As an alternative to creating your own search, have you considered using Algolia? They provide a free tier and an excellent React client library: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react/ – Gordon Burgett Mar 09 '20 at 14:07

2 Answers2

3

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 => ..........
Austin Wolfe
  • 349
  • 1
  • 3
  • 5
  • Thank for reading the question. The entity in charge of operating the search is `` but as of now there are no property linked. I think your solution is good but how do I link that to the ``? – Emanuele Mar 06 '20 at 20:30
  • I updated the question and added **VesselSearch.js** if that could be useful. – Emanuele Mar 06 '20 at 20:33
0

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.

Arjun
  • 143
  • 1
  • 15
  • Thank you very much for reading the question :) . Could you please include some code to integrate with my existing code so that I can better understand your solution? – Emanuele Mar 06 '20 at 20:09
  • can I get the link to your github repo ? – Arjun Mar 06 '20 at 20:10
  • 1
    Unfortunately it is a big project and could not really create a sandbox, also I cannot ask you to go through the entire code, but I thank you for offering that. I did update the question and added **VesselSearch.js** that was missing before. Maybe that could be helpful too? – Emanuele Mar 06 '20 at 20:36
  • You don't have to create the sandbox! You just push it to the github repo. – Arjun Mar 06 '20 at 21:02