-
Notifications
You must be signed in to change notification settings - Fork 38
React Code Conventions
This page outlines a few core conventions for the React codebase. Some of these rules will be enforced in ESLint, however some may not be. Please ensure code you write and review follows the conventions listed here.
Components should live in files named the same as the component. Component names and their files should be PascalCase.
Typically, only one component should live in a single file. Additional components are okay if their usage is limited to the main component of that file (i.e. prefer to export only a single component).
Only functional components should be used (no class components unless strictly needed).
A component declaration should look like this:
import React from "react"
export interface MyComponentProps {
name: string;
}
const MyComponent: React.FC<MyComponentProps> = ({ name }) => {
return <div>{name}</div>
}
export default MyComponent
If you use VS Code, the following can be added to your User Snippets file (Bottom left cog > user snippets):
"Create a typescript default export functional react component": {
"prefix": "rtse",
"body": [
"import React from \"react\"",
"",
"export interface ${1:$TM_FILENAME_BASE}Props {}",
"",
"const ${1:$TM_FILENAME_BASE}: React.FC<${1:$TM_FILENAME_BASE}Props> = () => {",
" $0",
" return <div></div>",
"}",
"",
"export default $TM_FILENAME_BASE"
]
}
This will allow you to quickly create a react component with the above style when you type "rtse".
The most important directories for the project are as follows:
/src - root directory of all source code
/src/pages - contains the main pages of the application (for routing)
/src/components/{page} - contains all the components used by {page}
/src/components/common - components used by multiple pages and components
/src/hooks - custom react hooks
/src/services - code for third party things
/src/styles - home for root css code
/src/util - any other random code
/public - files to be copied directly to the root of the website
Prefer to split a single unit into a controller component containing the logic and a view component purely for the presentation of the data from the controller. This allows for the separate development of the view and the logic.
When designing the view for a component, all required external data should be passed in as props. Any actions that the user takes (i.e. button clicks) should execute a callback passed to the child via props. When developing views, you can simply pass in dummy data via props to ensure your design looks as it should.
When designing the controller for a component, typically the only returned JSX would be the view component with its props passed. In the controller, handle effects, state, data fetching etc. When developing controllers, you can create a dummy view component that simply displays the data from the controller and tests callbacks to ensure things work as expected.
Once the view and controller are done, replacing the view that is being rendered by the controller to the real one, and passing down real props, should be all that is required to get a complete unit.
In summary, the view of the component should be quite "dumb". It doesn't really know anything other than what it was told in its props. The controller can have a much greater understanding of what is going on in the application, but itself should not do much in the way of changing the DOM.
This is a simple example of this pattern. The component is a simple text input that displays a sharing code (fetched from the API), and a button that will copy that text to your clipboard.
// ShareLinkController.tsx
import React from 'react';
import { useApi } from '../../hooks/useApi';
import { useFlatContext } from '../../hooks/useFlatContext';
import ShareLinkView from './ShareLinkView';
interface ShareLinkControllerProps {}
const ShareLinkController: React.FC<ShareLinkControllerProps> = () => {
// Controller gets information from Context
const { flatId } = useFlatContext();
// Controller gets information from API
const { data: sharingLink } = useApi('/api/flat/link', {
method: 'GET',
queryParams: { flatId },
});
// Controller defines the logic for how the copying to clipboard is done
const onCopy = async () => {
await navigator.clipboard.writeText(sharingLink);
};
// Controller passes this data to the view
return <ShareLinkView onCopy={onCopy} sharingLink={sharingLink} />;
};
export default ShareLinkController;
// ShareLinkView.tsx
import React from 'react';
import { ClipboardCopyIcon } from '@heroicons/react/outline';
export interface ShareLinkViewProps {
// The view declares the data it needs
sharingLink: string;
onCopy: () => void;
}
const ShareLinkView: React.FC<ShareLinkViewProps> = ({
sharingLink,
onCopy,
}) => {
return (
<div className="flex gap-x-2 items-center">
<input className="rounded p-2" readOnly>
{/* The view displays this data however it wants to */}
{sharingLink}
</input>
<ClipboardCopyIcon
className="text-white p-1 rounded w-8"
// The view alerts the controller of actions taken, which are then handled in the controller.
onClick={onCopy}
/>
</div>
);
};
export default ShareLinkView;
Rather than having the View component hard coded in the controller, the controller instead declares a render function as a prop. This means the creator of the Controller can then pass in the desired view. The controller then simply calls that function with the props.
// Wherever the controller is used
return <ShareLinkController render={ShareLinkView} /> // Using ShareLinkView
return <ShareLinkController render={AnotherShareLinkView} /> // Shares the logic, but a different view
// Inside the controller
interface ShareLinkControllerProps {
render(props: ShareLinkViewProps): void
}
const ShareLinkController: React.FC<ShareLinkControllerProps> = ({ render }) => {
// ...
return render({sharingLink, onCopy})
}
- Home
-
Repo Documents
- Introduction and Tools
- Project Proposal
- Contributions (Team 3, Team 4)
-
Specifications
- Team 3 Feature Specifications for A2
- Team 4 Feature Specifications for A1
- Tips and Tricks
-
Internal Documentations
-
Team 3 Documentation for A2
- Subteam 1
- Subteam 2
- Subteam 3
- Team 3 Whole Meetings
-
Team 4 Documentation for A1
- Team Agreement
- Meeting Minutes - Front End
- Meeting Minutes - Back End
- Meeting Minutes - Whole Team
- Recordings
-
Team 3 Documentation for A2
- Repository Setup