Prevent rerender home menu while typing
Asked Answered
D

2

6

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

Disbranch answered 14/7, 2020 at 21:46 Comment(2)
Could you create a codesandbox to reproduce your problem? Or review the posted code? I tried to reproduce with the piece of code you posted but couldn't do it. For example, your <SearchBar> component has references to this.props, but it doesn't have any props...Signesignet
Oh yes sorry here's the codesandbox codesandbox.io/s/keen-ritchie-t5kn8?file=/src/index.js i will edit my post tooDisbranch
S
1

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.

Streamy answered 14/7, 2020 at 22:31 Comment(4)
Thank you for answering quickly. The empty string ``` resultDatas``` is the way I found so that by typing a new search, the display does not generate the error display automatically. I would like to keep the search display while the user types a new search in the field without the display changing before the form is submitted. For example if you type "bulbasaur", subit and type a new search, i would like the display of bulbasaur stay until the next submitting.Disbranch
I've updated my answer with additional information. I've also updated the CodeSandbox to display the needed changes.Streamy
That totally work !! Thank you very much Chris it was the behavior that I wanted so badly, and thank you for the explications i understand better where's the problemDisbranch
@EdwinLandsfield No problem!Streamy
E
1

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>
            )
       
        
    }
}
Erinnerinna answered 15/7, 2020 at 0:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.