如何在reactjs中为所有子页面创建通用的页眉和页脚?

时间:2020-03-16 20:11:18

标签: reactjs material-ui

我在应用程序中使用reactjs,我需要添加“注册”页面。

我想要一个具有通用页眉和页脚页面的父级布局。

当任何人单击“注册”按钮时,将放置相同的通用页眉和页脚。

-------------------Header.js------------------

import React from 'react';
import { fade,makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
// import Grid from '@material-ui/core/Grid';
import Icon from '@material-ui/core/Icon';
// import { browserHistory, Router, Route } from 'react-router';
import './App.css'; 
/* AppBar*/
//import { fade, makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import InputBase from '@material-ui/core/InputBase';
import Badge from '@material-ui/core/Badge';
import MenuItem from '@material-ui/core/MenuItem';
import Menu from '@material-ui/core/Menu';
import MenuIcon from '@material-ui/icons/Menu';
import SearchIcon from '@material-ui/icons/Search';
import AccountCircle from '@material-ui/icons/AccountCircle';
import MailIcon from '@material-ui/icons/Mail';
import NotificationsIcon from '@material-ui/icons/Notifications';
import MoreIcon from '@material-ui/icons/MoreVert';
/*App bar */

// import  ImgMediaCard from './ImgMediaCard';

// import GridList from '@material-ui/core/GridList';
// import GridListTile from '@material-ui/core/GridListTile';

// import DemoCarousel from './MCarousel';

// var gridListStyle = {  
//     marginLeft: "40px"
//   };

  const useStyles = makeStyles(theme => ({
    grow: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      display: 'none',
      [theme.breakpoints.up('sm')]: {
        display: 'block',
      },
    },
    search: {
      position: 'relative',
      borderRadius: theme.shape.borderRadius,
      backgroundColor: fade(theme.palette.common.white, 0.15),
      '&:hover': {
        backgroundColor: fade(theme.palette.common.white, 0.25),
      },
      marginRight: theme.spacing(2),
      marginLeft: 0,
      width: '100%',
      [theme.breakpoints.up('sm')]: {
        marginLeft: theme.spacing(3),
        width: 'auto',
      },
    },
    searchIcon: {
      width: theme.spacing(7),
      height: '100%',
      position: 'absolute',
      pointerEvents: 'none',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
    },
    inputRoot: {
      color: 'inherit',
    },
    inputInput: {
      padding: theme.spacing(1, 1, 1, 7),
      transition: theme.transitions.create('width'),
      width: '100%',
      [theme.breakpoints.up('md')]: {
        width: 200,
      },
    },
    sectionDesktop: {
      display: 'none',
      [theme.breakpoints.up('md')]: {
        display: 'flex',
      },
    },
    sectionMobile: {
      display: 'flex',
      [theme.breakpoints.up('md')]: {
        display: 'none',
      },
    }  
  }));
  /*end App bar work*/
  const useGridStyles = makeStyles(theme => ({
    root: {
      flexGrow: 1,
    },
    paper: {
      // padding: theme.spacing(2),
      // textAlign: 'center',
      // color: theme.palette.text.secondary,
    },
  }));

function Header({ history }) {
    const Gridclasses = useGridStyles();
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);
  const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);

  const isMenuOpen = Boolean(anchorEl);
  const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);

  const handleProfileMenuOpen = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleMobileMenuClose = () => {
    setMobileMoreAnchorEl(null);
  };

  const handleMenuClose = () => {
    setAnchorEl(null);
    handleMobileMenuClose();
  };

  const handleMobileMenuOpen = event => {
    setMobileMoreAnchorEl(event.currentTarget);
  };

  const onRegisterClick = () => {
    debugger;    
      history.push('/Register');      
 };

  const menuId = 'primary-search-account-menu';
  const renderMenu = (
    <Menu
      anchorEl={anchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      id={menuId}
      keepMounted
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      open={isMenuOpen}
      onClose={handleMenuClose}
    >
      <MenuItem onClick={handleMenuClose}>Profile</MenuItem>
      <MenuItem onClick={handleMenuClose}>My account</MenuItem>
    </Menu>
  );

  const mobileMenuId = 'primary-search-account-menu-mobile';
  const renderMobileMenu = (
    <Menu
      anchorEl={mobileMoreAnchorEl}
      anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
      id={mobileMenuId}
      keepMounted
      transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      open={isMobileMenuOpen}
      onClose={handleMobileMenuClose}
    >
      <MenuItem>   
      <Icon className="fa fa-plus-circle" />    
     </MenuItem>
      <MenuItem>     
        <IconButton aria-label="show 4 new mails" color="inherit">
          <Badge badgeContent={4} color="secondary">
            <MailIcon />
          </Badge>
        </IconButton>
        <p>Messages</p>
      </MenuItem>
      <MenuItem>
        <IconButton aria-label="show 11 new notifications" color="inherit">
          <Badge badgeContent={11} color="secondary">
            <NotificationsIcon />
          </Badge>
        </IconButton>
        <p>Notifications</p>
      </MenuItem>
      <MenuItem onClick={handleProfileMenuOpen}>
        <IconButton
          aria-label="account of current user"
          aria-controls="primary-search-account-menu"
          aria-haspopup="true"
          color="inherit"
        >
          <AccountCircle />
        </IconButton>
        <p>Profile</p>
      </MenuItem>
    </Menu>
  );
  return (

  <Paper className={Gridclasses.paper}>

      <div className={classes.grow}>
      <AppBar position="static">
        <Toolbar>
          <IconButton
            edge="start"
            className={classes.menuButton}
            color="inherit"
            aria-label="open drawer"
          >
            <MenuIcon />
          </IconButton>
          <Typography className={classes.title} variant="h6" noWrap>
            Shopping Center
          </Typography>
          <div className={classes.search}>
            <div className={classes.searchIcon}>
              <SearchIcon />
            </div>
            <InputBase
              placeholder="Search…"
              classes={{
                root: classes.inputRoot,
                input: classes.inputInput,
              }}
              inputProps={{ 'aria-label': 'search' }}
            />
          </div>
          <div className={classes.grow} />
          <div className={classes.sectionDesktop}>          
          <IconButton  aria-label="" color="inherit" onClick={onRegisterClick}> Sign Up             
            </IconButton> 
            <IconButton aria-label="show 4 new mails" color="inherit">
              <Badge badgeContent={4} color="secondary">
                <MailIcon />
              </Badge>
            </IconButton>
            <IconButton aria-label="show 17 new notifications" color="inherit">
              <Badge badgeContent={17} color="secondary">
                <NotificationsIcon />
              </Badge>
            </IconButton>
            <IconButton
              edge="end"
              aria-label="account of current user"
              aria-controls={menuId}
              aria-haspopup="true"
              onClick={handleProfileMenuOpen}
              color="inherit"
            >
              <AccountCircle />
            </IconButton>
          </div>
          <div className={classes.sectionMobile}>
            <IconButton
              aria-label="show more"
              aria-controls={mobileMenuId}
              aria-haspopup="true"
              onClick={handleMobileMenuOpen}
              color="inherit"
            >
              <MoreIcon />
            </IconButton>
          </div>
        </Toolbar>
      </AppBar>
      {renderMobileMenu}
      {renderMenu}
       </div>
        </Paper>
    );
}

export default Header;




-------------------Register.js----------------

import React from 'react';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/core/styles';
import IconButton from '@material-ui/core/IconButton';
import Input from '@material-ui/core/Input';
import FilledInput from '@material-ui/core/FilledInput';
import OutlinedInput from '@material-ui/core/OutlinedInput';
import InputLabel from '@material-ui/core/InputLabel';
import InputAdornment from '@material-ui/core/InputAdornment';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import TextField from '@material-ui/core/TextField';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';

const useStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
    flexWrap: 'wrap',
  },
  margin: {
    margin: theme.spacing(1),
  },
  withoutLabel: {
    marginTop: theme.spacing(3),
  },
  textField: {
    width: 200,
  },
}));

function RegisterBuyer() {
  const classes = useStyles();
  const [values, setValues] = React.useState({
    amount: '',
    password: '',
    weight: '',
    weightRange: '',
    showPassword: false,
  });

  const handleChange = prop => event => {
    setValues({ ...values, [prop]: event.target.value });
  };

  const handleClickShowPassword = () => {
    setValues({ ...values, showPassword: !values.showPassword });
  };

  const handleMouseDownPassword = event => {
    event.preventDefault();
  };

  return (
    <div className={classes.root}>
      <div>
        <TextField
          label="With normal TextField"
          id="standard-start-adornment"
          className={clsx(classes.margin, classes.textField)}
          InputProps={{
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}
        />
        <FormControl className={clsx(classes.margin, classes.withoutLabel, classes.textField)}>
          <Input
            id="standard-adornment-weight"
            value={values.weight}
            onChange={handleChange('weight')}
            endAdornment={<InputAdornment position="end">Kg</InputAdornment>}
            aria-describedby="standard-weight-helper-text"
            inputProps={{
              'aria-label': 'weight',
            }}
          />
          <FormHelperText id="standard-weight-helper-text">Weight</FormHelperText>
        </FormControl>
        <FormControl className={clsx(classes.margin, classes.textField)}>
          <InputLabel htmlFor="standard-adornment-password">Password</InputLabel>
          <Input
            id="standard-adornment-password"
            type={values.showPassword ? 'text' : 'password'}
            value={values.password}
            onChange={handleChange('password')}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  aria-label="toggle password visibility"
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                >
                  {values.showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
          />
        </FormControl>
        <FormControl fullWidth className={classes.margin}>
          <InputLabel htmlFor="standard-adornment-amount">Amount</InputLabel>
          <Input
            id="standard-adornment-amount"
            value={values.amount}
            onChange={handleChange('amount')}
            startAdornment={<InputAdornment position="start">$</InputAdornment>}
          />
        </FormControl>
      </div>
      <div>
        <TextField
          label="With normal TextField"
          id="filled-start-adornment"
          className={clsx(classes.margin, classes.textField)}
          InputProps={{
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}
          variant="filled"
        />
        <FormControl className={clsx(classes.margin, classes.textField)} variant="filled">
          <FilledInput
            id="filled-adornment-weight"
            value={values.weight}
            onChange={handleChange('weight')}
            endAdornment={<InputAdornment position="end">Kg</InputAdornment>}
            aria-describedby="filled-weight-helper-text"
            inputProps={{
              'aria-label': 'weight',
            }}
          />
          <FormHelperText id="filled-weight-helper-text">Weight</FormHelperText>
        </FormControl>
        <FormControl className={clsx(classes.margin, classes.textField)} variant="filled">
          <InputLabel htmlFor="filled-adornment-password">Password</InputLabel>
          <FilledInput
            id="filled-adornment-password"
            type={values.showPassword ? 'text' : 'password'}
            value={values.password}
            onChange={handleChange('password')}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  aria-label="toggle password visibility"
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                  edge="end"
                >
                  {values.showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
          />
        </FormControl>
        <FormControl fullWidth className={classes.margin} variant="filled">
          <InputLabel htmlFor="filled-adornment-amount">Amount</InputLabel>
          <FilledInput
            id="filled-adornment-amount"
            value={values.amount}
            onChange={handleChange('amount')}
            startAdornment={<InputAdornment position="start">$</InputAdornment>}
          />
        </FormControl>
      </div>
      <div>
        <TextField
          label="With normal TextField"
          id="outlined-start-adornment"
          className={clsx(classes.margin, classes.textField)}
          InputProps={{
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}
          variant="outlined"
        />
        <FormControl className={clsx(classes.margin, classes.textField)} variant="outlined">
          <OutlinedInput
            id="outlined-adornment-weight"
            value={values.weight}
            onChange={handleChange('weight')}
            endAdornment={<InputAdornment position="end">Kg</InputAdornment>}
            aria-describedby="outlined-weight-helper-text"
            inputProps={{
              'aria-label': 'weight',
            }}
            labelWidth={0}
          />
          <FormHelperText id="outlined-weight-helper-text">Weight</FormHelperText>
        </FormControl>
        <FormControl className={clsx(classes.margin, classes.textField)} variant="outlined">
          <InputLabel htmlFor="outlined-adornment-password">Password</InputLabel>
          <OutlinedInput
            id="outlined-adornment-password"
            type={values.showPassword ? 'text' : 'password'}
            value={values.password}
            onChange={handleChange('password')}
            endAdornment={
              <InputAdornment position="end">
                <IconButton
                  aria-label="toggle password visibility"
                  onClick={handleClickShowPassword}
                  onMouseDown={handleMouseDownPassword}
                  edge="end"
                >
                  {values.showPassword ? <Visibility /> : <VisibilityOff />}
                </IconButton>
              </InputAdornment>
            }
            labelWidth={70}
          />
        </FormControl>
        <FormControl fullWidth className={classes.margin} variant="outlined">
          <InputLabel htmlFor="outlined-adornment-amount">Amount</InputLabel>
          <OutlinedInput
            id="outlined-adornment-amount"
            value={values.amount}
            onChange={handleChange('amount')}
            startAdornment={<InputAdornment position="start">$</InputAdornment>}
            labelWidth={60}
          />
        </FormControl>
      </div>
    </div>
  );
}   

export default  Register

-------------------Index.js-------------------
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router,Switch, Route } from "react-router-dom";
import './index.css';
import Header from './Header.js'
import App from './App';
import * as serviceWorker from './serviceWorker';
import RegisterBuyer from './RegisterBuyer';

ReactDOM.render(   
    <Router>
       <Header />
    <Switch>
  <Route path="/RegisterBuyer" component={RegisterBuyer} />
  <Route exact path="/" component={App} />
  </Switch>
</Router>
   , document.getElementById('root'));

我是一个有反应的新手。我应该在我的代码中做什么?

我从这里获取了菜单和卡片的示例代码:https://material-ui.com

Application built with

{
  "react": "16.13.0", 
  "react-dom": "^16.13.0", 
  "react-redux": "^7.2.0",
  "redux": "^4.0.4"
  "@material-ui/core": "^4.9.5"
}

我面临的问题是以下功能出现错误:

const onRegisterClick = () => {
    debugger;    
      history.push('/Register');      
 };

以上功能显示以下错误: TypeError:无法读取未定义的属性“ push”

HTMLUnknownElement.callCallback node_modules / react-dom / cjs / react-dom.development.js:188 invokeGuardedCallbackDev node_modules / react-dom / cjs / react-dom.development.js:237 invokeGuardedCallback node_modules / react-dom / cjs / react-dom.development.js:292 invokeGuardedCallbackAndCatchFirstError node_modules / react-dom / cjs / react-dom.development.js:306 executeDispatch node_modules / react-dom / cjs / react-dom.development.js:389 executeDispatchesInOrder node_modules / react-dom / cjs / react-dom.development.js:414 executeDispatchesAndRelease node_modules / react-dom / cjs / react-dom.development.js:3278 executeDispatchesAndReleaseTopLevel node_modules / react-dom / cjs / react-dom.development.js:3287 forEachAccurated node_modules / react-dom / cjs / react-dom.development.js:3259 runEventsInBatch node_modules / react-dom / cjs / react-dom.development.js:3304 runExtractedPluginEventsInBatch node_modules / react-dom / cjs / react-dom.development.js:3514 handleTopLevel node_modules / react-dom / cjs / react-dom.development.js:3558 batchedEventUpdates $ 1 node_modules / react-dom / cjs / react-dom.development.js:21902 batchedEventUpdates node_modules / react-dom / cjs / react-dom.development.js:1060 dispatchEventForLegacyPluginEventSystem node_modules / react-dom / cjs / react-dom.development.js:3568 tryToDispatchEvent node_modules / react-dom / cjs / react-dom.development.js:4267 dispatchEvent /node_modules/react-dom/cjs/react-dom.development.js:4189 stable_runWithPriority node_modules / scheduler / cjs / scheduler.development.js:653 runWithPriority $ 1 node_modules / react-dom / cjs / react-dom.development.js:11061 离散更新$ 1 node_modules / react-dom / cjs / react-dom.development.js:21918 离散更新 node_modules / react-dom / cjs / react-dom.development.js:1071 dispatchDiscreteEvent /node_modules/react-dom/cjs/react-dom.development.js:4168 输出:SignUp组件应在布局(父项)中显示,并具有相同的公共页眉和页脚。

我试图遵循指南并查找示例实现,但无法很好地解决/解释问题。

1 个答案:

答案 0 :(得分:1)

编辑:我已经对其进行了更新,以与您的“母版”和“子版”页面概念保持一致。


反应是基于组件的,因此要在一组其他组件旁边或周围呈现的任何组件都应采用这种结构。

例如,您想将A = [...S].forEach(char => {}); 组件呈现在页面其余部分的上方。因此,将您的<?php $myArray = [ 'x' => <<<'STR' \relative c' { \key ees \major bes'2 \mf c bes4. (as8) g4 (as) \breathe \bar "|." STR ]; echo $myArray['x']; 显示在页面内容旁边是有意义的:

Header

在这里,我们在Header组件中定义了两个组件。 const MasterPage = () => { return ( <> <Header /> <ChildPage /> </> ) } const ChildPage = () => { return ( <div> {/* ...child page content goes here... */} </div> ) } 呈现在MasterPage组件上方。

Header组件包含子页面的内容,您可以随意选择。无论您在ChildPage内进行什么渲染,ChildPage都将始终被渲染。

这意味着您不必在每个子页面组件中重复呈现标题。

ChildPageHeader标记是React.Fragment的简写,这意味着您不必将<></>包装在额外的Header,但您可以轻松编写:

ChildPage

您可以为div做同样的事情:

<div>
  <Header />
  <ChildPage />
</div>

您看到的错误是因为您需要导入Footer。当前是<> <Header /> <ChildPage /> <Footer /> </> ,因此您正尝试推送到未定义的数组。