moroshko / react-autosuggest

WAI-ARIA compliant React autosuggest component
http://react-autosuggest.js.org
MIT License
5.97k stars 586 forks source link

Uncaught TypeError: Cannot read property 'trim' of undefined #368

Open jopcode opened 7 years ago

jopcode commented 7 years ago

When i type in the search bar this show me the results, but when i click on a value the console give me this 'Uncaught TypeError: Cannot read property 'trim' of undefined', and my app don't reload the view until I deleted the value on search bar. Any idea or solution? ( sorry for my bad english)

Screenshots:

before type on the search bar:

image

When i type on search bar style (doesn't matter) image

And when i click on a value(the progress bar and the time don't reload until after deleted value on search bar): image

When i deleted the value on search bar: image

First TypeError: image

And the last TypeError:

shouldRenderSuggestions @   AutosuggestContainer.js:174
componentWillReceiveProps   @   Autosuggest.js:72
(anonymous function)    @   ReactCompositeComponent.js:611
measureLifeCyclePerf    @   ReactCompositeComponent.js:75
updateComponent @   ReactCompositeComponent.js:610
receiveComponent    @   ReactCompositeComponent.js:547
receiveComponent    @   ReactReconciler.js:125
_updateRenderedComponent    @   ReactCompositeComponent.js:754
_performComponentUpdate @   ReactCompositeComponent.js:724
updateComponent @   ReactCompositeComponent.js:645
receiveComponent    @   ReactCompositeComponent.js:547
receiveComponent    @   ReactReconciler.js:125
_updateRenderedComponent    @   ReactCompositeComponent.js:754
_performComponentUpdate @   ReactCompositeComponent.js:724
updateComponent @   ReactCompositeComponent.js:645
receiveComponent    @   ReactCompositeComponent.js:547
receiveComponent    @   ReactReconciler.js:125
updateChildren  @   ReactChildReconciler.js:109
_reconcilerUpdateChildren   @   ReactMultiChild.js:208
_updateChildren @   ReactMultiChild.js:312
updateChildren  @   ReactMultiChild.js:299
_updateDOMChildren  @   ReactDOMComponent.js:936
updateComponent @   ReactDOMComponent.js:754
receiveComponent    @   ReactDOMComponent.js:716
receiveComponent    @   ReactReconciler.js:125
_updateRenderedComponent    @   ReactCompositeComponent.js:754
_performComponentUpdate @   ReactCompositeComponent.js:724
updateComponent @   ReactCompositeComponent.js:645
receiveComponent    @   ReactCompositeComponent.js:547
receiveComponent    @   ReactReconciler.js:125
updateChildren  @   ReactChildReconciler.js:109
_reconcilerUpdateChildren   @   ReactMultiChild.js:208
_updateChildren @   ReactMultiChild.js:312
updateChildren  @   ReactMultiChild.js:299
_updateDOMChildren  @   ReactDOMComponent.js:936
updateComponent @   ReactDOMComponent.js:754
receiveComponent    @   ReactDOMComponent.js:716
receiveComponent    @   ReactReconciler.js:125
_updateRenderedComponent    @   ReactCompositeComponent.js:754
_performComponentUpdate @   ReactCompositeComponent.js:724
updateComponent @   ReactCompositeComponent.js:645
performUpdateIfNecessary    @   ReactCompositeComponent.js:561
performUpdateIfNecessary    @   ReactReconciler.js:157
runBatchedUpdates   @   ReactUpdates.js:150
perform @   Transaction.js:140
perform @   Transaction.js:140
perform @   ReactUpdates.js:89
flushBatchedUpdates @   ReactUpdates.js:172
closeAll    @   Transaction.js:206
perform @   Transaction.js:153
batchedUpdates  @   ReactDefaultBatchingStrategy.js:62
enqueueUpdate   @   ReactUpdates.js:200
enqueueUpdate   @   ReactUpdateQueue.js:24
enqueueSetState @   ReactUpdateQueue.js:219
ReactComponent.setState @   ReactComponent.js:63
handleSongPlaying   @   App.js:55
whileplaying    @   index.js:153
SMSound._whileplaying   @   soundmanager2.js:3421
SMSound._onTimer    @   soundmanager2.js:2953
(anonymous function)    @   soundmanager2.js:4106
(anonymous function)    @   soundmanager2.js:3852

The line 55 of my app.js:

handleSongPlaying(audio) {
        this.setState({
            elapsed: this.parseMilliseconds(audio.position),
            total: this.parseMilliseconds(audio.duration),
            position: audio.position / audio.duration
        })
    }

The states;

this.state = {
            track: {
                stream_url: '',
                title: '',
                artwork_url: ''
            },
            playStatus: Sound.status.PLAYING,
            elapsed: '00:00',
            total: '00:00',
            position: 0,
            playFromPosition: 0,
            autoCompleteValue: '',
            tracks: []
        }

Any idea or solution for this? 🤕

moroshko commented 7 years ago

It's hard to say from the information you provided what's wrong.

This error could appear if somehow inputProps.value ends up not being a string. Could you verify that it is always a string?

If you still can't figure this out, could you create a Codepen that reproduces the issue?

jopcode commented 7 years ago

@moroshko where i could verify that is always a string?,this is mi full code:


import React, { Component } from 'react'
import ReactDom from 'react-dom'
import Axios from 'axios'
import Sound from 'react-sound'
import update from 'react-addons-update'

import Details from '../components/Details'
import Player from '../components/Player'
import Progress from '../components/Progress'
import Search from '../components/Search'

export default class App extends Component {
    constructor() {
        super()
        this.clientId = 'H1mb6cSHjS0dg9AvapNHmczj9zFtbcgJ'
        this.state = {
            track: {
                stream_url: '',
                title: '',
                artwork_url: ''
            },
            playStatus: Sound.status.PLAYING,
            elapsed: '00:00',
            total: '00:00',
            position: 0,
            playFromPosition: 0,
            autoCompleteValue: '',
            tracks: []
        }
    }

    componentDidMount() {
        this.randomSong();
    }

    onSuggestionsClearRequested() {

    }

    handleChange(event, value) {
        this.setState({ autoCompleteValue: value.newValue })
        let _this = this

        Axios.get(`https://api.soundcloud.com/tracks?client_id=${this.clientId}&q=${value.newValue}`)
            .then((res) => {
                _this.setState({ tracks: res.data })
                console.log(_this.state.track)
            })
            .catch((err) => {
                console.log(err)
            })
    }

    handleSelect(value, item) {
        this.setState({ autoCompleteValue: value.item, track: value })
    }

    handleSongPlaying(audio) {
        this.setState({
            elapsed: this.parseMilliseconds(audio.position),
            total: this.parseMilliseconds(audio.duration),
            position: audio.position / audio.duration
        })
    }

    handleSoundFinished() {
        this.randomSong()
    }

    parseMilliseconds(time) {
        let hours = Math.floor(time / 3600000)
        time = time % 3600000

        let minutes = Math.floor(time / 60000)
        time = time % 60000

        let seconds = Math.floor(time / 1000)
        time = Math.floor(time % 1000)

        return (
            (minutes < 10 ? '0' : '') + minutes + ':' +
            (seconds < 10 ? '0' : '') + seconds
        )
    }

    togglePlay() {
        if (this.state.playStatus == Sound.status.PLAYING) {
            this.setState({ playStatus: Sound.status.PAUSE })
        } else {
            this.setState({ playStatus: Sound.status.PLAYING })
        }
    }

    stop() {
        this.setState({ playStatus: Sound.status.STOPPED })
    }

    forward() {
        this.setState({ playFromPosition: this.state.playFromPosition += 1000 * 10 })
    }

    backward() {
        this.setState({ playFromPosition: this.state.playFromPosition -= 1000 * 10 })
    }

    randomSong() {

        let _this = this
        Axios.get('https://api.soundcloud.com/playlists/126995930?client_id=' + this.clientId)
            .then((res) => {
                const trackLength = res.data.tracks.length
                const randomNumber = Math.floor((Math.random() * trackLength) + 1)

                _this.setState({ track: res.data.tracks[randomNumber] })

                let track = _this.state.track
                track['artwork_url'] = _this.state.track.artwork_url.replace('large', 't500x500')
                _this.setState({ track: track })

            })
            .catch((err) => {
                console.log(err)
            })
    }

    formatUrl(url) {
        return `${url}?client_id=${this.clientId}`
    }

    render() {
        return (
            <div>
            <Sound
                url={this.formatUrl(this.state.track.stream_url)}
                playStatus={this.state.playStatus}
                onPlaying={this.handleSongPlaying.bind(this)}
                playFromPosition={this.state.playFromPosition}
                onFinishedPlaying={this.handleSoundFinished.bind(this)} />
            <Search
                autoCompleteValue={this.state.autoCompleteValue}
                tracks={this.state.tracks}
                handleSelect={this.handleSelect.bind(this)}
                handleChange={this.handleChange.bind(this)}
                onSuggestionsClearRequested={this.onSuggestionsClearRequested.bind(this)}
            />
            <section>
                <img src={this.state.track.artwork_url} width="400" height="225" />
                <header>
                    <Details
                        elapsed={this.state.elapsed}
                        title={this.state.track.title} />
                    <Player
                        togglePlay={this.togglePlay.bind(this)}
                        stop={this.stop.bind(this)}
                        forward={this.forward.bind(this)}
                        backward={this.backward.bind(this)}
                        random={this.randomSong.bind(this)}
                        playerStatus={this.state.playStatus}
                    />
                </header>
                <Progress
                    position={this.state.position} />
            </section>
            </div >
        )
    }

}

i am a bit new on react
thisfrontenddev commented 7 years ago

You can check this in your AutosuggestContainer.js, or maybe your Search.js file.

What you should be checking is that the value that you pass as an argument to inputProps is a string value.

If you followed the examples in the doc, you should have something like this :

const inputProps = {
      placeholder: 'Type a programming language',
      value,
      onChange: this.onChange
};

where value is set by

const getSuggestionValue = suggestion => suggestion.name;

If you find the place where the value is set, you can check that you never return a value that is not a string. As an example, I had the same problem because I was returning suggestion.name but my data was on a different model, so I had to change to something like suggestion.myobject.myproperty.

m-czarnik commented 7 years ago

I'm having the same issue as described here, except I'm 100% correct that my inputProps.value is always string. Using 9.1.0 version of react-autosuggest.

moroshko commented 7 years ago

@Mateusz-Czarnik Could you create a Codepen that reproduces the issue?

m-czarnik commented 7 years ago

Yes, I will try tomorrow.

philipstanislaus commented 7 years ago

I stumbled accross the same error. In my case was not a bug, but a misunderstanding of the onChange handler:

I used an onChange handler as I would on every other input:

onChange = (event) => {
  this.setState({ value: event.target.value })
}

While the Autosuggest component provides the new value not within event.target, but in the second parameter:

onChange = (event, { newValue }) => {
  this.setState({ value: newValue })
}

I just submitted a minor PR that includes a hint in the documentation: https://github.com/moroshko/react-autosuggest/pull/396

philipstanislaus commented 7 years ago

@paliwalgaurav can you show your onChange handler?

daffinito commented 7 years ago

Throwing this up here for anyone else that may run into this error. I ran into this and it ended up being my getSuggestionValue const. I had:

const getSuggestionValue = suggestion => {
  suggestion.username;
};

but it should have been:

const getSuggestionValue = suggestion => suggestion.username;
makostom commented 7 years ago

This worked for me , in case somebody needs it

onChange = (event, { newValue }) => {
        this.setState({
            value: typeof newValue !== 'undefined' ? newValue : '',
        });
};
renanborgez commented 6 years ago

Same here, the problem is when we have a value like null or undefined if i set my value to empty string '' it works,

Autossugest.js line 280

return (valueBeforeUpDown || value).trim();

it should be some thing like this

const newValue = valueBeforeUpDown || value; 
return newValue ? newValue.trim() : null;
dafapramudya commented 5 years ago

Hi @philipstanislaus i got undefined variable newValue. This is my code:

<Input type="text" placeholder="Username" autoComplete="username" name="username" onChange={(e, {newValue}) => this.setState({username: newValue})}/>

philipstanislaus commented 5 years ago

@dafapramudya code looks fine – did you try this solution? https://github.com/moroshko/react-autosuggest/issues/368#issuecomment-341709014

u4yk commented 5 years ago

I've been getting this error on the getSuggestionValue callback. Turned out, this.setState wasn't updating my state variable for some reason. The callback got invoked. The context is correct. Everything should work, but this.setState just wasn't working properly. Perhaps, it's the async nature of this.setState and my call got queued, but I don't know for certain if it was or not.

I ended up assigning the value to my state variable directly and calling this.forceUpdate() directly afterwards in my callback. I know it's not the cleanest solution, but it works. Anyone else running into this problem should check to make sure this.setState is actually updating your variable.

I don't know if the issue is being caused by this component, but I post this in case someone else runs into this problem.

fozerol commented 5 years ago

Throwing this up here for anyone else that may run into this error. I ran into this and it ended up being my getSuggestionValue const. I had:

const getSuggestionValue = suggestion => {
  suggestion.username;
};

but it should have been:

const getSuggestionValue = suggestion => suggestion.username;

this solved my case, thank you , can you explain what is the difference between them, i am new in react and cant see any difference

u4yk commented 5 years ago

@fozerol IIRC in the first example, the arrow function is just executing suggestion.username, which does nothing since it's a string. Without the braces, like in the second example, suggestion.username gets returned, which is why it works and the first one doesn't. I never use the non-braced version and explicitly return my values; so, I might not be the best to explain this, but that's how it works if memory serves.

invot commented 5 years ago

What fixed it for me is the following:

 const getSuggestions = (input:any) => {
     const inputValue = input.value.trim().toLowerCase();
     const inputLength = inputValue.length;
     return inputLength === 0 ? [] : data.filter(lang =>
       lang.name.toLowerCase().slice(0, inputLength) === inputValue
     );
 };
ghost commented 5 years ago

What fixed it for me is the following:

 const getSuggestions = (input:any) => {
     const inputValue = input.value.trim().toLowerCase();
     const inputLength = inputValue.length;
     return inputLength === 0 ? [] : data.filter(lang =>
       lang.name.toLowerCase().slice(0, inputLength) === inputValue
     );
 };

tried this solution but I still have the same error

Shivendra30 commented 4 years ago

The issue is the with onChange handler only. I changed it to the following:

 <Autosuggest
            suggestions={nameSuggestions}
            onSuggestionsFetchRequested={onSuggestionsFetchRequested}
            onSuggestionsClearRequested={onSuggestionsClearRequested}
            getSuggestionValue={getSuggestionValue}
            renderSuggestion={renderSuggestion}
            inputProps={{
              value: name,
              onChange: (e, { newValue }) => setName(newValue),
            }}
          />
Phinah commented 3 years ago

having the same problem dont know how to solve this

function checkRequired(input) { input.forEach(function(input) { if (input.value.trim() === '') { showError(input, ${getFieldName(input)} is required); } else { showSuccess(input); } }); }