是否可以通过从其道具之一调用函数来获取嵌套的内部React Component的名称?

时间:2019-02-06 23:40:56

标签: reactjs material-ui

我有这个组件:

class DashboardPage extends Component {
  constructor(props) {
    super(props);

    this.state = {
      loading: true,
      shownPage: ActiveDeals,
      error: false,
      errorDetails: null,
      activeIcon: "Home"
    };
  }

  componentDidMount() {
    //
  }

  setShownPage = (name, iconName) => () => {
    this.setState({ shownPage: name, activeIcon: iconName });
  };

  getIconColor = () => {
    // could I call this from the Home component and check its name? Or know the caller?
    return "primary";
  };

  render() {
    const { classes } = this.props;

    const menuItems = (
      <List>
        <ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(ActiveDeals, "Home")}>
          <ListItemIcon className={classNames(classes.listItemIcon)}>
            <Home color={this.state.activeIcon === "Home" ? "primary" : "secondary"} />
          </ListItemIcon>
        </ListItem>
        <ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(UpcomingDates, "CalendarToday")}>
          <ListItemIcon className={classNames(classes.listItemIcon)}>
            <CalendarToday color={this.state.activeIcon === "CalendarToday" ? "primary" : "secondary"} />
          </ListItemIcon>
        </ListItem>
        <ListItem className={classNames(classes.listItem)} button onClick={this.setShownPage(DealsPipeline, "FilterList")}>
          <ListItemIcon className={classNames(classes.listItemIcon)}>
            <FilterList color={this.state.activeIcon === "FilterList" ? "primary" : "secondary"} />
          </ListItemIcon>
        </ListItem>
      </List>
    );

    return (
      <MainFrame
        route={this.props.match.url}
        title={this.state.shownPage.title}
        menuItems={menuItems}
        open={this.state.open}
        topRightFeature={this.state.shownPage.topRightFeature}
      >
        <this.state.shownPage />
        <div>Primary color is {this.props.theme.palette.primary.main}</div>
      </MainFrame>
    );
  }
}

export default withStyles(styles, { withTheme: true })(DashboardPage);

...我习惯于在后端语言中使用nameof()type()来知道给定实例的名称。在React中,我尚未找到实现此目的的方法。

我不是基于状态来设置图标颜色(它使用硬编码的字符串,难),而是想要一种实用的方法来遍历dom树以找到相对的子代,或者只知道调用图标的名称的方法getIconColor方法,以便可以将其与活动状态进行比较。

在使用“知道”例如被调用的函数的组件时,组件是否可以设置属性? Home

2 个答案:

答案 0 :(得分:1)

如果将函数绑定在使用该函数的组件中,则可以获得调用者的身份。这仅在类组件中有效。

类似这样的东西:

class Apple extends React.Component {
  constructor(props) {
    super(props);
    this.getName = props.getName.bind(this);
  }
  render() {
    return <div>I am an {this.getName()}</div>;
  }
}

class Banana extends React.Component {
  getName() {
    return this.constructor.name;
  }
  render() {
    return (
      <div className="App">
        <Apple getName={this.getName} />
      </div>
    );
  }
}

https://codesandbox.io/s/247lpxl4j0

答案 1 :(得分:1)

我认为您正在尝试以一种错过React的声明能力和组件组合提供的可能性的方式来解决此问题。列表项之间的代码重复恳求引入另一个组件:

const listItemStyles = {
  listItem: {
    /* whatever styles you need */
  },
  listItemIcon: {
    /* whatever styles you need */
  }
};
const DashboardListItem = withStyles(listItemStyles)(
  ({ Page, Icon, ShownPage, classes, setShownPage }) => {
    return (
      <ListItem
        className={classes.listItem}
        button
        onClick={() => setShownPage(Page)}
      >
        <ListItemIcon className={classes.listItemIcon}>
          <Icon color={ShownPage === Page ? "primary" : "secondary"} />
        </ListItemIcon>
      </ListItem>
    );
  }
);

然后您的menuItems变为:

const menuItems = [
  { Page: ActiveDeals, Icon: Home },
  { Page: UpcomingDates, Icon: CalendarToday },
  { Page: DealsPipeline, Icon: FilterList }
];
const mappedMenuItems = menuItems.map((menuItem, index) => (
  <DashboardListItem
    key={index}
    {...menuItem}
    ShownPage={this.state.shownPage}
    setShownPage={this.setShownPage}
  />
));

完整代码如下:

import React, { Component } from "react";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import { withStyles } from "@material-ui/core/styles";
import Home from "@material-ui/icons/Home";
import CalendarToday from "@material-ui/icons/CalendarToday";
import FilterList from "@material-ui/icons/FilterList";
const styles = {};
const ActiveDeals = () => {
  return <div>ActiveDeals Page!</div>;
};
const UpcomingDates = () => {
  return <div>UpcomingDates Page!</div>;
};
const DealsPipeline = () => {
  return <div>DealsPipeline Page!</div>;
};
const listItemStyles = {
  listItem: {
    /* whatever styles you need */
  },
  listItemIcon: {
    /* whatever styles you need */
  }
};
const DashboardListItem = withStyles(listItemStyles)(
  ({ Page, Icon, ShownPage, classes, setShownPage }) => {
    return (
      <ListItem
        className={classes.listItem}
        button
        onClick={() => setShownPage(Page)}
      >
        <ListItemIcon className={classes.listItemIcon}>
          <Icon color={ShownPage === Page ? "primary" : "secondary"} />
        </ListItemIcon>
      </ListItem>
    );
  }
);
const menuItems = [
  { Page: ActiveDeals, Icon: Home },
  { Page: UpcomingDates, Icon: CalendarToday },
  { Page: DealsPipeline, Icon: FilterList }
];

class DashboardPage extends Component {
  constructor(props) {
    super(props);

    this.state = {
      shownPage: ActiveDeals
    };
  }

  setShownPage = page => {
    this.setState({ shownPage: page });
  };

  render() {
    const mappedMenuItems = menuItems.map((menuItem, index) => (
      <DashboardListItem
        key={index}
        {...menuItem}
        ShownPage={this.state.shownPage}
        setShownPage={this.setShownPage}
      />
    ));
    return (
      <div>
        <List>{mappedMenuItems}</List>

        <this.state.shownPage />
        <div>Primary color is {this.props.theme.palette.primary.main}</div>
      </div>
    );
  }
}

export default withStyles(styles, { withTheme: true })(DashboardPage);

这是一个可行的示例:

Edit DashboardPage