Home > Back-end >  Unable to access data passed in through react router
Unable to access data passed in through react router

Time:06-25

I'm trying to pass in data that I received from my backend to another page on my frontend. I'm using react-router-dom and withRouter to pass my data from the product.js page to the result.js page on the frontend. I keep getting this error though:

Uncaught TypeError: Cannot read properties of undefined (reading 'state')

And the code always breaks on this line of code, it doesn't go past it (this code is present in the result.js page code down below):

<div className="score">hAPPi SCORE: <br/> <span>{this.props.location.state.data.data.score}</span></div>

(product.js PAGE) Here's the code of the piece in the react page that passes the received data from the backend to another page. What this code does is basically take in a user's input (through speech), send the transcript to the backend, and the backend will shoot out feedback, scores, etc..., and that data will then come back to this product.js page. From there, I want to pass that data to the result.js page.

import './Product.css';
import React, { Component } from 'react';
import Scale from './Scale.js';
import Loader from './Loader.js';
import { withRouter } from "react-router-dom";

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition()

recognition.continous = true
recognition.interimResults = true
recognition.lang = 'en-US'

class Product extends Component {
    constructor() {
        super()
        this.state = {
          // Transcript and speech recognition variables
          listening: false, 

          text: "", 
          score: "", 

          // Initial main score value
          scoreScaleValue: "50", 

          // Backend API data
          data: {"data": {
              "score": 12, 
              "keywords": [], 
              "entities": [], 
              "feedback": "",
              "tones": {"joy": null, "fear": null, "sadness": null, "anger": null}
          }},

          // Loading state
          loading: false,
        }
        this.toggleListen = this.toggleListen.bind(this)
        this.handleListen = this.handleListen.bind(this)
        this.updateScoreScale = this.updateScoreScale.bind(this);
    }

    updateScoreScale() {
        this.interval = setInterval(() => {
            var scale = document.getElementById("scr-scale");
            console.log(parseInt(this.state.data.data.score), parseInt(scale.value));
            if (parseInt(scale.value) < parseInt(this.state.data.data.score)) {
                this.setState({scoreScaleValue: this.state.scoreScaleValue - -1}) 
            } else {
                this.setState({scoreScaleValue: this.state.scoreScaleValue -  1}) 
            }
            
            if (parseInt(scale.value) === parseInt(this.state.data.data.score)) {
                console.log("stop");
                clearInterval(this.interval);
            }
        }
        
        , 20);
    }


    // Handles sending the transcript and receiving the json object from the backend
    handleSending() {
        const options = {
            method: "POST",
            headers: {
                'Content-Type': 'application/json;charset=utf-8', 
            },
            body: JSON.stringify(this.state.text)
        };

        // Setting loading screen equal to true
        this.setState({loading: true});

        // Fetching data from the backend
        fetch("http://127.0.0.1:5000/", options)
            .then(response=> response.json())
            .then(json => this.setState({data: json}))

            // Redirect user to results page
            // .then(this.handleRedirect())

            // Once data is retrieved from the backend, stop loading screen
            .finally(() => {
                this.setState({loading: false})
                this.handleRedirect()
            })
        
        // Resetting the transcript
        this.setState({text: ""})
    }
    
    handleRedirect() {
        // Redirect user to the results page
        console.log(this.state.data)
        console.log(this.state.data.data.feedback)

        this.props.history.push("/results", {data: this.state.data}); // Sending backend data to frontend

    }

    // Updates the scale
    handleScaleUpdate() {
        this.updateScoreScale()
        console.log(this.state.text.length, "LENGTH")
        // console.log(this.state.data)
    }
 
    toggleListen() {
        this.setState({
            listening: !this.state.listening
        }, this.handleListen)
    }

    handleListen() {
        console.log('listening?', this.state.listening)

        if (this.state.listening) {
            recognition.start()
            recognition.onend = () => {
                recognition.start()
            }
            
        } else {
            recognition.stop()
            recognition.onend = () => {
                // Checking if the transcript has words in it
                if (this.state.text.length >= 1) {
                    this.handleSending()
                    console.log("success")
                }
                else {
                    // Resetting the transcript
                    this.setState({text: ""})
                }
        }
    }

        recognition.onresult = event => {
            let interimTranscript = ''

            for (let i = event.resultIndex; i < event.results.length; i  ) {
                const transcript = event.results[i][0].transcript;
                if (event.results[i].isFinal) this.setState({text: this.state.text  = transcript   ' '}) ;
                else interimTranscript  = transcript;
            }

            // document.getElementById('interim').innerHTML = interimTranscript 
        }
        
        recognition.onerror = event => {
            console.log("Error occurred in recognition: "   event.error)
        }
    }

    render() {
        return (
            <div>
                {/* Show loading screen while fetching data and calling API requests in the backend */}
                {this.state.loading ? (
                    <div className="Container">
                        <Loader/>
                    </div>
                ) : (
                    <div className="Container">
                        <div className="record-section">
                            <div className="record-title ">Talk to us, tell us about your day today...</div>
                            <div className="transcript-section box-shadow-container">
                                {this.state.text}
                            </div>

                            <div className="record-buttons box-shadow-container">
                                <button className='redo-btn btn' onClick={() => {this.setState({text: ""})}}>Redo</button>
                                <button className="start-session-btn btn" onClick={this.toggleListen}>{this.state.listening ? "Submit" : "Start Session"}</button>
                            </div>
                        </div>

                        <div className="test">
                        
                        </div>  
                    </div>
                )}
            </div>
          
        
        );
    }

}



export default withRouter(Product);

(result.js PAGE) Here's the code of the react page that is supposed to receive the data from the first page and display it on the page:

import './Product.css';
import React, { Component } from 'react';
import Scale from './Scale.js';


class ResultPage extends Component {
    constructor() {
        super()
    
    this.state = {
      // Initial main score value
      scoreScaleValue: "50", 
    }
}


render() {
    console.log(this.state.data) // trying to console log it, but it won't work
    return (
        
        <div className="Container">
            <div className="result-section">
                <div className="score">hAPPi SCORE: <br/> <span>{this.props.location.state.data.data.score}</span></div>
                <div className="scale-container">
                    <Scale scale-id="scr-scale" style="score-scale" score={this.props.location.state.data.data.score}/>

                </div>
            </div>

            <div className="analysis-section">
                <div className="analysis-container">
                    <div className="analysis-title">
                        In-Depth Analysis
                    </div>
                    <div className="keywords-container">
                        <div className="section-titles">
                            Keywords
                        </div>
                        <div>
                            <ul>
                                {this.props.location.state.data.data.keywords.map(kw =>
                                    <h3>{kw}</h3>
                                )}
                            </ul>
                        </div>
                    </div>

                    <div className="entity-container">
                        <div className="section-titles">
                            Entities
                        </div>
                        <div>
                            <ul>
                                {this.props.location.state.data.data.entities.map(en =>
                                    <h3>{en}</h3>
                                )}
                            </ul>
                        </div>
                    </div>

                    <div>
                        <h1>Result</h1>
                        <div>
                            {this.props.location.state.data.data.feedback}
                        </div>
                    </div>
                    
                    <hr></hr>
                </div>
                
            </div>
        </div> 
    
    );
}

}



export default ResultPage;

I keep getting errors whenever I use the this.props.location.state.data.(something) but when I replace that with hard-coded numbers/strings, the page works fine. I'm not sure I'm doing anything wrong in terms of passing the data from the product.js page to the result.js page, but I'm pretty sure I'm doing something wrong in accessing that data from the result.js page using this.props.location....

Some help would be greatly appreciated!

CodePudding user response:

You haven't shown where/how the ResultPage component is rendered. It can receive the location object as a prop in a couple ways.

  1. Rendered directly by a Route component on one of the rendering props.

    Example:

    • <Route path="/result" component={ResultPage} />
      
    • <Route
        path="/result"
        render={routeProps => <ResultPage {...routeProps} />}
      />
      
      
  2. Decorated with the withRouter Higher Order Component.

    Example:

    import { withRouter } from 'react-router-dom';
    
    class ResultPage extends Component {
      ...
    }
    
    export default withRouter(ResultPage);
    

With the route props passed/injected, the ResultPage component should now be able to access a this.props.location.state?.data value passed in route state from the product page.

  • Related