1
\$\begingroup\$

I've made a simple app that shows if the term entered into the search bar is present in a number of common English words.

This is the stackblitz: https://stackblitz.com/edit/react-mostusedwords

Note how App, SearchBar and ResultsDisplay all have a variable to track the current search. I am looking for a better way as this seems repetitive and I can't imagine that it is good practice.

App.js

import React, { Component } from 'react';
import { render } from 'react-dom';
import SearchBar from './Components/SearchBar/SearchBar';
import ResultsDisplay from './Components/ResultsDisplay/ResultsDisplay';
import './style.css';
import mostCommonWords from './MostCommonWords';

class App extends Component {
  state = {
    results: [],
    currentSearch: ''
  }

  onSearch = (term) => {
    let list = [];

    if(term !== '')
      for(let x = 0; x < mostCommonWords.length; x++)
        if(mostCommonWords[x].includes(term))
          list.push(mostCommonWords[x])

    this.setState({ results: list, currentSearch: term });
  }

  render() {
    return (
      <div>
        <h1 className="app-header1">Search 1000 common English words</h1>
        <SearchBar onSearch={ this.onSearch } />
        <ResultsDisplay
          currentSearch={ this.state.currentSearch }
          results={ this.state.results }
        />
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

mostCommonWords is just an array of words in a separate file.

SearchBar.js

import React, { Component } from 'react';
import './SearchBar.css';

export default class SearchBar extends React.Component {

  state = { searchTerm: 'as' }

  componentDidMount() {
    this.doSearch(this.state.searchTerm);
  }

  onInputChange = e => {
    const newValue = e.target.value;
    this.setState({ searchTerm: newValue });

    this.doSearch(newValue);
  }

  doSearch(value){
    setTimeout(
      () => {
        /** if value not equal, user has changed it already, so do not search */
        if(value === this.state.searchTerm)
          this.props.onSearch(value);
      },
      500
    );
  }

  onFormSubmit = e => {
    e.preventDefault();
  }

  render() {
    return (
      <div className="search-bar">
        <form onSubmit={ this.onFormSubmit }>
          <h2>Enter search term:</h2>
          <input 
            name="theInputField"
            value={ this.state.searchTerm }
            onChange = { this.onInputChange }
            />

          <div className="buttonDiv">

            {/** This button does not yet */}
            <button
              type="button"
              onClick = { e => { this.onFormSubmit(e) } }
              >Search</button>

            <button
              type="button"
              onClick = { () => { this.setState({ searchTerm: ''}) } }
              >Clear</button>
          </div>
        </form>
      </div>
    );
  }
}

ResultsDisplay.js

import React, { Component } from 'react';
import Result from './Result';
import './ResultsDisplay.css';

export default class ResultsDisplay extends React.Component {
  state = {
    totalItems: 0,
    jsxData: [],
    currentSearch: ''
  };

  componentDidUpdate(prevProps, prevState) {
    if(this.props.currentSearch == prevProps.currentSearch)
      return;

    this.updateColumns(this.props.results);
  }

  updateColumns = (newItems) => {
    if(!newItems || newItems.length === 0) {
      this.setState({ 
        jsxData: [], 
        totalItems: 0, 
        currentSearch: this.props.currentSearch
      });
      return;
    }

    let totalColumns = 4;
    let totalItems = newItems.length;
    let itemsPerColumn = Math.ceil(totalItems / totalColumns);
    let rows = [];

    for(let x = 0; x < itemsPerColumn; x++) {
      rows.push([]);
    }

    for(let it = 0; it < itemsPerColumn; it++) {
      for(let col = 0; col < totalColumns; col++) {
        let item = newItems[col * itemsPerColumn + it];
        console.log(col * itemsPerColumn + it);
        rows[it].push(
          <Result index={col * itemsPerColumn + it} key={item} text={item} />
        );
      }
    }

    let newJsx = [];

    for(let col = 0; col < itemsPerColumn; col++) {
      newJsx.push(
        <tr>{rows[col]}</tr>
      );
    }

    this.setState({ 
      jsxData: newJsx,
      totalItems: totalItems,
      currentSearch: this.props.currentSearch 
    });
  }

  render(props) {
    return (
      <div className="results-display">
        { this.showItems() }
      </div>
    );
  }

  showItems = () => {
    if(this.state.totalItems === 0) { 
      return (
        <div>
          <h2>No results</h2>
          <em>No or invalid search. Try a (different) search and press enter.</em>
        </div>
      )
    }

    return (
      <div>
        <h2>{ this.state.totalItems } results found</h2>
         <table><tbody>
          { this.state.jsxData }
          </tbody></table>
      </div>
    );
  }
}

Result.js

import React, { Component } from 'react';
import './Result.css';

export default class Result extends React.Component {

  render(props) {
    if(!this.props.text)
      return (<td></td>);

    return (
      <td className="result">
        { +this.props.index + 1 }: { this.props.text }
      </td>
    );
  }
}

I need the variable to compare in the update function, but can't this be more tightly linked? In blog posts I've found this requires implementing quite copious amounts of code for something that seems simple.

I'd also appreciate any input on other improvements on the code.

\$\endgroup\$
  • \$\begingroup\$ Hi D3Duck, you need to input your code in your post, not on a third party link :) \$\endgroup\$ – IEatBagels Oct 16 '19 at 15:42
  • \$\begingroup\$ @IEatBagels Thank you. Fixed that. \$\endgroup\$ – D3Duck Oct 16 '19 at 22:28
  • \$\begingroup\$ can it be taken off hold now? Question was fixed \$\endgroup\$ – D3Duck Oct 17 '19 at 4:19
  • \$\begingroup\$ Good job on fixing your question, it's great now I hope you get answers :) \$\endgroup\$ – IEatBagels Oct 18 '19 at 13:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.