Best practices for the React UI framework.
Components should do one thing. If they are doing many things, then consider breaking them up into subcomponents.
Are there state updates in loops?
When importing a component that is exported “by default”, do not rename the component. The code is more understandable if every component is always referenced by its original name.
Consider destructuring props. This makes the code clearer by identifying exactly which properties are of interest in the function signature.
When using withTracker, define a const to compute each property, then put the properties in the return using object shorthand notation. For example:
const StudentHomeIcePageContainer = withTracker(() => {
const { username } = useParams();
const studentID = Users.getProfile(username).userID;
const earnedICE = StudentProfiles.getEarnedICE(username);
const projectedICE = StudentProfiles.getProjectedICE(username);
const helpMessages = HelpMessages.findNonRetired({});
const favoriteInterests = FavoriteInterests.findNonRetired({ userID: studentID });
const courseInstances = CourseInstances.findNonRetired({ studentID });
const opportunityInstances = OpportunityInstances.findNonRetired({ studentID });
return {
helpMessages,
earnedICE,
projectedICE,
favoriteInterests,
courseInstances,
opportunityInstances,
};
})(StudentIcePage);
Some of our components get data from collections in the render method. This is not reactive. For example:
const AdvisorPageMenuWidget = () => {
const match = useRouteMatch();
const { username } = useParams();
const divStyle = { marginBottom: 30 };
const profile = AdvisorProfiles.getProfile(username);
let numMod = 0;
numMod += Reviews.findNonRetired({ moderated: false }).length;
let moderationLabel = 'Moderation';
if (numMod > 0) {
moderationLabel = `${moderationLabel} (${numMod})`;
}
let numRequests = 0;
numRequests += VerificationRequests.findNonRetired({ status: 'Open' }).length;
numMod
and numRequests
are not reactive.
Many React components are exported “by default”, which gives the importing client the ability to rename them in the file that they are used in.
The convention in RadGrad is to import a component with a name that matches the file name used to define the component. Let’s look at a simple example:
// File: AdminAnalyticsNewsletterWidget.ts
const AdminAnalyticsNewsletterWidget = () => {
:
:
}
const AdminAnalyticsNewsletterWidgetContainer = connect(mapStateToProps, mapDispatchToProps)(AdminAnalyticsNewsletterWidget);
export default AdminAnalyticsNewsletterWidgetContainer;
In this case, we have a component (AdminAnalyticsNewsletterWidget), defined in a file called “AdminAnalyticsNewsletterWidget.ts”, but the actual exported object is a wrapped version of the widget called AdminAnalyticsNewsletterWidgetContainer.
Our convention is to import this component in the following way:
import AdminAnalyticsNewsletterWidget from '../../components/admin/analytics/newsletter/AdminAnalyticsNewsletterWidget';
In other words, we name the imported component using the name associated with the file, and not the “containerized” name.
class AdminHomeBanner extends React.Component {
render() {
const gridStyle = { height: '500px' };
return (
<div className="adminhome-banner">
<Grid container verticalAlign="middle" textAlign="center" style={gridStyle}>
<Grid.Row>
<Grid.Column>
<div className="welcome-text"><p>Hello there</p></div>
<Header as="h1" inverted>
Manage your site
</Header>
</Grid.Column>
</Grid.Row>
</Grid>
</div>
);
}
}
Should be
const AdminHomeBanner = () => {
const gridStyle = { height: '500px' };
return (
<div className="adminhome-banner">
<Grid container verticalAlign="middle" textAlign="center" style={gridStyle}>
<Grid.Row>
<Grid.Column>
<div className="welcome-text"><p>Hello there</p></div>
<Header as="h1" inverted>
Manage your site
</Header>
</Grid.Column>
</Grid.Row>
</Grid>
</div>
);
}
Many React components are named with “Widget”. In most (all?) cases, adding “Widget” just increases the length of the name without adding value.
In addition, many component names contain the word “Card”. Only use “Card” in a class name when it literally returns a single Semantic UI Card.