I'm trying to build a web application menu dynamically using react's new context API, json as data, Providers, Consumers etc. I can't figure out an error I get. But first here's some code:
Context file includes:
import React from 'react';
export const MenuContext = React.createContext();
class MenuProvider extends React.Component {
state = {
menu: {}
};
actions = {
fetchMenu: async() => {
const response = await fetch('/admin/json/menu.json');
const body = await response.json();
this.setState({
menu: body
});
}
}
componentDidMount() {
this.actions.fetchMenu();
}
render() {
return(
<MenuContext.Provider value={{state: this.state, actions: this.actions}}>
{this.props.children}
</MenuContext.Provider>
)
}
}
export default MenuProvider;
The menu component consumes this context as follows:
import React from 'react';
import {v1 as uuid} from 'uuid';
import MenuProvider, {MenuContext} from './contexts/menu';
import MenuDropdown from './parts/menu-dropdown';
import MenuItem from './parts/menu-item';
export default class SideMenu extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<header>
<MenuProvider>
<MenuContext.Consumer>
{({state}) => (
<React.Fragment>
<div className="cnt-menu-head">
<h6>{state.menu.heading}</h6>
</div>
<div className="cnt-menu">
<ul className="nav nav-pills flex-column">
{state.menu.sections.map((section, i) => {
if (section.type === 'link') {
return <MenuItem exact={section.exact} linkTo={section.linkTo} linkText={section.linkText}/>
} else if (section.type === 'dropdown') {
var keyedLinks = section.links.map((link) => {
link.key = uuid();
return link;
});
return <MenuDropdown key={uuid} linkText={section.linkText} links={keyedLinks}/>
}
})}
</ul>
</div>
</React.Fragment>
)}
</MenuContext.Consumer>
</MenuProvider>
</header>
);
};
};
menu.json file has this data:
{
"heading": "Menu Heading",
"sections": [
{
"type": "link",
"linkText": "A Link",
"linkTo": "/",
"exact": true
},
{
"type": "dropdown",
"linkText": "System",
"links": [
{
"linkText": "System Table",
"linkTo": "/table/system",
"exact": false
},
{
"linkText": "Add System",
"linkTo": "/form/addsystem",
"exact": false
}
]
}
]
}
Versions of packages I use in package.json are:
"devDependencies": {
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-es2016": "^6.24.1",
"babel-preset-react": "^6.24.1",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-router-dom": "^4.3.1",
"uuid": "^3.3.0"
},
And .babelrc
file is:
{
"presets": [
["es2016"],
"react"
],
"plugins": [
"transform-class-properties"
]
}
Compilation of this application runs without errors, though I get a javascript error in console when I run it:
Uncaught TypeError: Cannot read property 'map' of undefined
The property in question here is state.menu.sections
which I want to loop through and render based on type. If I comment out the part I loop state.menu.sections
with map, the part <h6>{state.menu.heading}</h6>
works flawlessly and renders <h6>Menu Heading</h6>
without any errors. What may be the problem when I loop the sections array?
Try this code:
import React from 'react';
export const MenuContext = React.createContext();
class MenuProvider extends React.Component {
state = {
menu: { heading: '', sections: [] }
};
actions = {
fetchMenu: async() => {
const response = await fetch('/admin/json/menu.json');
const body = await response.json();
this.setState({
menu: body
});
}
}
componentDidMount() {
this.actions.fetchMenu();
}
render() {
return(
<MenuContext.Provider value={{state: this.state, actions: this.actions}}>
{this.props.children}
</MenuContext.Provider>
)
}
}
export default MenuProvider;
The difference is menu
in state
, instead of empty menu
object, you need to provide it with default values.
NOTE: I not sure about the error, but I think it is because javascript try to use map
on state.menu.sections
before the componentDidMount
set new state to your state
(so the state.menu.sections
will be undefined
by default)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.