Home > Back-end >  React: Uncaught TypeError: Cannot read properties of undefined
React: Uncaught TypeError: Cannot read properties of undefined

Time:08-27

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>
    )
}
  • Related