Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent rerender home menu while typing

I'm having a problem with my searchbar component. When performing a search, the request is successful and we get the desired display, however if we want to do a new search over it we automatically return to the main menu during typing. Can you tell me how to keep the display of the previous search without returning? Thank you in advance

import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'

class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            pokemon: '',
            resultDatas : '',
            search: false,
            whiteList: [],
            error:''
        }
        this.handleChange = this.handleChange.bind(this)
        this.handleClick = this.handleClick.bind(this)
        this.handleSubmit = this.handleSubmit.bind(this)
        this.fillWhiteList()
    }

    //white list function to forbid all special characters
    fillWhiteList() {
        axios.get('https://pokeapi.co/api/v2/pokemon/')
        .then(list => {
            const resultList =  list.data.results
            const theList = resultList.map(theList => theList.name.toLowerCase())
            this.setState({
                whiteList: theList
            })
        })
    }

    //handle the value of the text field and keep letter in lower case
    //deletion of white space in typing to not generate errors
    //async to have the last letter of the value
    handleChange = async function(e) {
        await this.setState({
            pokemon: e.target.value.toLowerCase().trim(),
            resultDatas:''
        })
    }

    //Call API function and data table recovery with 'resultDatas' state
    handleSubmit =  (e) => {
        e.preventDefault()
         axios.get('https://pokeapi.co/api/v2/pokemon/' + this.state.pokemon
        )
        .then( res => {
            console.log(res);
            const response = res.data
            this.onResetField()
            this.setState({
                resultDatas: response,
                search:true,
                error: res.status
            })
            console.log(this.state.error);
        })
    }

    //Home button function
    handleClick() {
        this.setState({
            resultDatas : '',
            search: false,
            pokemon: ''
        })
    }

    //clear input function when onClick in the cross icon
    onResetField() {
        const iconReset = document.querySelector('input')
        iconReset.value = ''
    }

    render(){
        // Home page display when no value is submitted
        if (this.state.search === false || this.state.resultDatas === '') {
            return (
                <div>
                    <SearchBar
                        value={this.handleChange}
                        submit={this.handleSubmit}
                        placeholder="Search Pokemon"
                        />
                    <Global
                        loading="Loading..."
                    />
                </div>
            )
            //Error display
        }else if (!this.state.whiteList.includes(this.state.pokemon) || this.state.error !== 200) {
           return(
               <div>
                   <SearchBar
                       submit={this.handleSubmit}
                       value={this.handleChange}
                       placeholder="Search Pokemon"
                   />
                   <Error
                        wrong="Pokemon not found please retry"
                   />
                   <PreviousButton
                        previously={this.handleClick}
                        return="Back to menu"
                   />
               </div>
           )
            // pokemon display
        }else {
            return(
                <div>
                    <SearchBar
                        submit={this.handleSubmit}
                        value={this.handleChange}
                        placeholder="Search Pokemon"
                    />
                        <PreviousButton
                            previously={this.handleClick}
                            return="Back to menu"
                        />
                    <ItemList
                        list={this.state.resultDatas}
                    />
                </div>
            )
        }
    }
}

//homepage display
class Global extends Component {
    constructor() {
       super()
       this.state = {
           item:[],
           isLoaded:false
       }
    }

       //Api call for display  home page
        APICall() {
           axios.get('https://pokeapi.co/api/v2/pokemon/venusaur')
           .then(response => {

               this.setState({
                   item: response,
                   isLoaded:true
               })
           })
       }

   render() {
       // loading display if request not found
           if (!this.state.isLoaded) {
           return (
               <div>
                    <div>{this.props.loading}</div>
               </div>
          )
       }else {
           return(
               <div>
                    {this.state.item.name}
               </div>
           )
       }
   }
}

//Searchbar component
 class SearchBar extends Component{
    render(){
        return(
            <form onSubmit={this.props.submit} autoSave="off">
                <input
                    inputMode="tel"
                    required="required"
                    autoComplete="off"
                    type="text"
                    onChange={this.props.value}
                    id="searchbar"
                    placeholder={this.props.placeholder}
                />
            </form>
        )
    }
}

//list of Pokemon component

class ItemList extends Component{
    render(){
        return(
            <div>
                {this.props.list.name}
                <img src={this.props.list.sprites.front_default}  alt="pokemon"/>
            </div>
        )
    }
}

// Previous button component
 class PreviousButton extends Component{

    render(){
        return(
            <button onClick={this.props.previously}>{this.props.return}</button>
        )
    }
}

//error page component
 class Error extends Component{
    render(){
        return(
            <div>
                {this.props.wrong}
            </div>
        )
    }
}

ReactDOM.render(<App/>, document.querySelector('#root'))

here a codesandbox in case it doesn't work https://codesandbox.io/s/keen-ritchie-t5kn8?file=/src/index.js

like image 858
Edwin Landsfield Avatar asked Jul 14 '20 21:07

Edwin Landsfield


2 Answers

It looks like this is what is causing the issue.

//handle the value of the text field and keep letter in lower case
//deletion of white space in typing to not generate errors
//async to have the last letter of the value
handleChange = async function(e) {
    await this.setState({
        pokemon: e.target.value.toLowerCase().trim(),
        resultDatas:''
    })
}

When you handle your input value change, you are setting resultDatas to an empty string. In your render function your "home page" is checking to see if search is equal to false or if your resultDatas is an empty string, so as soon as you start typing in a new search, it resets resultData and takes you to the home page.

 if (this.state.search === false || this.state.resultDatas === '') {
    return (
        <div>
            <SearchBar
                value={this.handleChange}
                submit={this.handleSubmit}
                placeholder="Search Pokemon"
            />
            <Global
                loading="Loading..."
            />
        </div>
    )
}

To fix this, you could make it so typing doesn't automatically clear your resultDatas like so

handleChange = function(e) {
    this.setState({
        pokemon: e.target.value.toLowerCase().trim()
    })
}

**I removed the async/await keywords from this function as this.setState is a callback, and not a promise.

CodeSandbox with changes: https://codesandbox.io/s/crazy-rgb-lwzne?file=/src/index.js

Additional Information

If you don't want to be immediately directed to your error page then you will need keep track of the current pokemon you have searched for separate from the current value that is being typed in. I've updated my CodeSandbox above to include these additional changes.

If you look at the CodeSandbox, you will see that there is now a currentPokemon value in your state. Inside of the on submit function where you are setting state, I am then updating currentPokemon to be equal to this.state.pokemon. The final piece that pull this together is within the render function. You need to change your error page condition to check if your whitelist includes this.state.currentPokemon instead of this.state.pokemon.

like image 197
Chris Avatar answered Oct 16 '22 13:10

Chris


You should use search instead of resultDatas to indicate search box status

handleChange = async function(e) {
    await this.setState({
        pokemon: e.target.value.toLowerCase().trim(),
         search:false
    })
}

then

 render(){
        // Home page display when no value is submitted
       
            return (
                <div>
                    <SearchBar
                        value={this.handleChange}
                        submit={this.handleSubmit}
                        placeholder="Search Pokemon"
                        />
                   {!this.state.search  && <Global
                        loading="Loading..."
                    />}

                   {this.state.resultDatas && <ItemList
                        list={this.state.resultDatas}
                    />} 

                  .
                  .
                  .

                </div>
            )
       
        
    }
}
like image 41
Alex Avatar answered Oct 16 '22 12:10

Alex