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.
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} />} />
-
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.