I am trying to use React to extract a JSON data from my server and render it with two functions. But it seems that the two render functions cannot read the values in the json correctly. I'm sure that my data server is working correctly.
Error log:
Unhandled Runtime Error
Uncaught TypeError: Cannot read properties of undefined (reading 'title')
Source
http://localhost:8080/dist/App.js [:19:68]
TypeError: Cannot read properties of undefined (reading 'title')
at Question (http://localhost:8080/dist/App.js:19:68)
at renderWithHooks (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:16313:18)
at mountIndeterminateComponent (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:20077:13)
at beginWork (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:21590:16)
at beginWork$1 (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:27414:14)
at performUnitOfWork (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:26548:12)
at workLoopSync (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:26454:5)
at renderRootSync (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:26422:7)
at performSyncWorkOnRoot (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:26074:20)
at flushSyncCallbacks (http://localhost:8080/_snowpack/pkg/react-dom.v18.2.0.js:12050:22)
App component:
let prop
function App() {
const [item, setItems] = useState([])
useEffect(() => {
fetch('http://localhost:9090/')
.then((res) => res.json())
.then((resJson) => {
const data = JSON.parse(resJson)
setItems(data)
})
}, [])
prop = item
return (
<div>
<Question/>
<hr/>
</div>
)
}
Question component:
function Question() {
return (
<div className={"question"}>
<h1>{ prop.question.title }</h1>
<p className={"info"}>Created by user: { prop.question.create_by }</p><br/>
<p className={"info"}>On { Intl.DateTimeFormat('en-US', {year: 'numeric', month: '2-digit',day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit'}).format(new Date(prop.question.time)) }</p><br/>
<hr/>
<div dangerouslySetInnerHTML={{__html: prop.question.detail}}></div>
</div>
)
}
export default App;
The JSON data:
{
"question": {
"title": "Question",
"create_by": "AZ",
"time": 1661394765044,
"detail": "<h4>info</h4>"
},
"answers": [
{
"create_by": "baa",
"time": 1661394765044,
"detail": "<h4>abc</h4>"
}
]
}
CodePudding user response:
I think they are not pure functions, they are components, you cannot share global variables and that is not the way to pass data between components in React, the way to do it correctly is using a context or props, to pass through props I leave you an example of the following way, you should apply this in the other variables too, example:
function App() {
const [item, setItems] = useState([])
useEffect(() => {
fetch('http://localhost:9090/')
.then((res) => res.json())
.then((resJson) => {
const data = JSON.parse(resJson)
setItems(data)
})
}, [])
return (
<div>
<Question title={item.question.title}/>
<hr/>
</div>
)
}
function Question({title}) {
return (
<div className={"question"}>
<h1>{ title }</h1>
<p className={"info"}>Created by user: { prop.question.create_by }</p><br/>
<p className={"info"}>On { Intl.DateTimeFormat('en-US', {year: 'numeric', month: '2-digit',day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit'}).format(new Date(prop.question.time)) }</p><br/>
<hr/>
<div dangerouslySetInnerHTML={{__html: prop.question.detail}}></div>
</div>
)
}
export default App;
CodePudding user response:
First, in App
remove JSON.parse(resJson)
as it's already parsed by res.json()
. Use some loader while fetching the data as it happens asynchronously, and your component render first. For that give nothing to useState
as parameter so item
is undefined
at the beginning.
function App() {
const [item, setItems] = useState()
useEffect(() => {
fetch('http://localhost:9090/')
.then((res) => res.json())
.then((data) => {
setItems(data)
})
}, []);
if(!item) return <p>Fetching data...</p>
return (
<div>
<Question question = {item.question}/>
<hr/>
</div>
)
}
Second, you shouldn't be using global variables to render data in your components when using React, instead use props
and state
. To do so, change Question
component so it receives the data it needs as props
and notice it's passed in App
to it:
function Question(props) {
return (
<div className={"question"}>
<h1>{ props.question.title }</h1>
<p className={"info"}>Created by user: { props.question.create_by }</p><br/>
<p className={"info"}>On { Intl.DateTimeFormat('en-US', {year: 'numeric', month: '2-digit',day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit'}).format(new Date(props.question.time)) }</p><br/>
<hr/>
<div dangerouslySetInnerHTML={{__html: props.question.detail}}></div>
</div>
)
}