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

IT技术 reactjs material-ui
2021-05-22 07:18:52

我有这个组件:

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 中,我还没有找到一种方法来做到这一点。

不是根据状态设置图标颜色(它使用硬编码字符串,oop),我想要一种功能性的方法来遍历 dom 树以查找相关子项,或者只知道调用该getIconColor方法的图标的名称的方法我可以将其与活动状态进行比较。

有没有办法让组件在使用“知道”它是从 eg 调用的函数时设置属性Home

2个回答

我认为您试图以一种错过了 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);

这是一个工作示例:

编辑仪表板页面

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

像这样的东西:

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