I have this component:
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);
... I'm used to using nameof()
and type()
in backend languages to know the name a given instance. In React, I have yet to find a way to do this.
Instead of setting the icon colors based on state (which uses hardcoded strings, yuck), I want a functional way to either traverse the dom tree to find relative children, or a way to just know the name of the icon which calls the getIconColor
method so I can compare it to the active state.
Is there any way for a component to set a property while using a function that "knows" that it was called from e.g. Home
?
I think you're trying to solve this in a way that misses out on the declarative power of React and the possibilities that component composition provides. The code duplication between the list items is begging for another component to be introduced:
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>
);
}
);
Then your menuItems
becomes:
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}
/>
));
With the full code looking like this:
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);
Here's a working example:
You can get the identity of the caller if you bind the function in the component where it is used. This would work only in a class component.
Something like this:
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://stackoverflow.com/questions/54564250/is-it-possible-to-get-the-name-of-a-nested-inner-react-component-by-calling-a-f