从父级调用子组件函数时,React Material-UI抽屉无法解决问题

时间:2017-05-28 17:40:54

标签: javascript reactjs react-native material-ui

我有一个App.js的示例代码

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Child from './child';

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        <Child  ref={instance => { this.child = instance; }}/>
          <button onClick={() => { this.child.onAlert(); }}>Click</button>
      </div>
    );
  }
}

export default App;

和子组件一样

import React, { Component } from 'react';

class Child extends Component {
    state = {  }
    onAlert =()=>
        alert("hey");
    render() {
        return (
           <div> IM kid</div> 
        );
    }
}

export default Child;

当我点击App.js中的按钮时,我能够获得预期的输出 即,我可以调用子函数onAlert()

我在material-ui中使用相同的场景,并在需要从工具栏组件触发抽屉组件的地方做出反应

我的代码就像下面的Titlebar.js代码是我的父组件

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Toolbar from 'material-ui/Toolbar'
import AppBar from 'material-ui/AppBar';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';

import { withStyles, createStyleSheet } from 'material-ui/styles';
import Child from './child';



const styleSheet = createStyleSheet('Titlebar', () => ({

    root: {
        position: 'relative',
        width: '100%',
    },
    appBar: {
        position: 'relative',
    },
    flex: {
        flex: 1,
    }
}));

class TitleBar extends Component {
  render() {
        const classes = this.props.classes;
        return (
            <div className={classes.root}>
                <AppBar className={classes.appBar}>
                    <Toolbar>
                        <IconButton contrast onClick={() => { this.child.handleLeftOpen(); }}>
                            <MenuIcon />
                        </IconButton>
                        <Typography type="title" colorInherit className={classes.flex}>Productivity Dashboard</Typography>
                    </Toolbar>
                </AppBar>
                <Child  ref={instance => { this.child = instance; }}/>
            </div>
        )
    }
}

TitleBar.PropTypes={
    classes:PropTypes.object.isRequired,
}

export default withStyles(styleSheet)(TitleBar);

我的子组件代码Child.js位于

之下
import React, { Component } from 'react';
import { withStyles, createStyleSheet } from 'material-ui/styles';
import Drawer from 'material-ui/Drawer';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Divider from 'material-ui/Divider';
import InboxIcon from 'material-ui-icons/Inbox';
import DraftsIcon from 'material-ui-icons/Drafts';
import StarIcon from 'material-ui-icons/Star';
import SendIcon from 'material-ui-icons/Send';
import MailIcon from 'material-ui-icons/Mail';
import DeleteIcon from 'material-ui-icons/Delete';
import ReportIcon from 'material-ui-icons/Report';

const styleSheet = createStyleSheet('Child', {
  list: {
    width: 250,
    flex: 'initial',
  },
  listFull: {
    width: 'auto',
    flex: 'initial',
  },
});
class Child extends Component {
    state = {
    open: {
      top: false,
      left: false,
      bottom: false,
      right: false,
    },
  }
handleLeftOpen = () =>{ 
            console.log("im here")
            this.toggleDrawer('left', true);
        }
        handleLeftClose = () => this.toggleDrawer('left', false);
  toggleDrawer = (side, open) => {
    const drawerState = {};
    drawerState[side] = open;
    this.setState({ open: drawerState });
  };
    render() {
        const classes=this.props.classes;
        return (
           <Drawer
          open={this.state.open.left}
          onRequestClose={this.handleLeftClose}
          onClick={this.handleLeftClose}
        >
         <List className={classes.list} disablePadding>
          <ListItem button>
          <ListItemIcon>
            <InboxIcon />
          </ListItemIcon>
          <ListItemText primary="Inbox" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <StarIcon />
          </ListItemIcon>
          <ListItemText primary="Starred" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <SendIcon />
          </ListItemIcon>
          <ListItemText primary="Send mail" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <DraftsIcon />
          </ListItemIcon>
          <ListItemText primary="Drafts" />
        </ListItem>
        </List>
        <Divider />
        <List className={classes.list} disablePadding>
          <ListItem button>
          <ListItemIcon>
            <MailIcon />
          </ListItemIcon>
          <ListItemText primary="All mail" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <DeleteIcon />
          </ListItemIcon>
          <ListItemText primary="Trash" />
        </ListItem>
        <ListItem button>
          <ListItemIcon>
            <ReportIcon />
          </ListItemIcon>
          <ListItemText primary="Spam" />
        </ListItem>
        </List>
                 </Drawer>
        );
    }
}

export default withStyles(styleSheet)(Child);

这里,当我单击Tiltlbar组件中的IconButton时,我从父母调用handleLeftOpen()函数我没有得到预期的输出。我在控制台中收到如下错误

Uncaught TypeError: Cannot read property 'handleLeftOpen' of null
    at onClick (http://localhost:3000/static/js/bundle.js:90993:50)
    at Object.ReactErrorUtils.invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:17236:17)
    at executeDispatch (http://localhost:3000/static/js/bundle.js:17019:22)
    at Object.executeDispatchesInOrder (http://localhost:3000/static/js/bundle.js:17042:6)
    at executeDispatchesAndRelease (http://localhost:3000/static/js/bundle.js:16430:23)
    at executeDispatchesAndReleaseTopLevel (http://localhost:3000/static/js/bundle.js:16441:11)
    at Array.forEach (native)
    at forEachAccumulated (http://localhost:3000/static/js/bundle.js:17339:10)
    at Object.processEventQueue (http://localhost:3000/static/js/bundle.js:16644:8)
    at runEventQueueInBatch (http://localhost:3000/static/js/bundle.js:24266:19)

请检查代码并告知我是否需要更改

1 个答案:

答案 0 :(得分:1)

这里的区别在于您在第一个示例中导出:

export default Child;

在第二个示例中,您导出:

export default withStyles(styleSheet)(Child);

这将返回一个修饰组件,因此ref将放在此装饰组件上,而不是您的Child组件。要解决此问题,装饰组件接受名为innerRef的属性,以便您可以将ref传递给您自己的组件。所以要解决这个问题,你需要改变:

<Child  ref={instance => { this.child = instance; }}/>

<Child  innerRef={instance => { this.child = instance; }}/>