This is a Next.js project bootstrapped with create-next-app
.
-
Clone repository
git clone https://github.com/effixis/mauro
-
Switch on working branch
git checkout <branch-name>
-
Install dependencies
npm install
-
Start the development server
npm run dev
-
pages
Contains all page file (ex:login.tsx
serve path/login
) -
components
Contains all react components -
utils
Contains files that doesn't have a specific purpose, or that define multiple components and/or hooks (ex:Input.tsx
) -
hooks
Contains custom hooks
- General purpose types are defined in
/utils/types.tsx
- Types specific to a component/page are defined directly in the file
For each component, define an interface that specify the props.
-
Exemple
import { FC } from "react"; export interface FooProps { title: string } const Foo: FC<FooProps> = (props) => { return <p>{props.title}</p>; }; export default Foo;
-
In case of generic interface, declare the component as a function (see issue)
// Optional, if needs children import { PropsWithChildren } from "react"; export interface FooProps<T> { data: T[] } function Foo<T>(props: FooProps<T>) { return <p>Hello World</p>; } export default Foo;
There is a convention on imports for consistency purposes
Import Order
1. | next | 4. | other external libraries | 7. | components |
2. | react | 5. | utils | ||
3. | material-ui | 6. | hooks |
Import Order
1. | styles |
2. | components |
3. | icons |
4. | other |
-
Use tree-shaking with components as it doesn't impact production (see doc)
-
Don't use tree-shaking with icons as it slows down development startup times (see doc)
Example of imports
// mui
import { makeStyles } from "@material-ui/core/styles";
import {
Grid,
TextField,
} from "@material-ui/core";
import DvrIcon from "@material-ui/icons/Dvr";
import Autocomplete from "@material-ui/lab/Autocomplete";
Input.tsx is a module created to facilitate the gathering and validations of inputs.
It defines three components: useInput
, InputProvider
and InputField
. Each one is used
in different parts of the workflow.
The general workflow idea consists of three parts:
- Create an input object with the input data structure
- Set criterias that the inputs values should meet
- Validate the user inputs and display potential errors
- Example with data structure:
{
name: string
age: number
mail: string
}
- Create the input object
// create an 'input' object using the useInput hook
const input = useInput({
name: "", // defaults to ""
age: 0, // defaults to 0
mail: "[email protected]", // defaults to "test@..."
});
- Set validations criterias
<div>
...
{/*
InputProvider is a context provider (with value 'input')
it is used by the InputField components
*/}
<InputProvider value={input}>
{/* InputField render a TextField component */}
<InputField
field="name" // specify what field is handeln
label="Name" // put a label
/>
{/* The age field must be a valid number bigger than 0*/}
<InputField
field="age"
label="Age"
type="number" // add number criteria
min={0} // add minimum criteria
/>
{/* Set a custom criteria on mail*/}
<InputField
field="mail"
label="Mail"
customValidation={
(key, value) => {
// mail must contains a '@' char
if (value.indexOf("@") == -1) {
return { error: true, info: "Invalid mail address." };
} else {
return { error: false, info: " " };
}
}
}
/>
</InputProvider>
...
</div>
- Validate the user inputs
// example of validation on a submit callback
function onSubmit() {
// input.validate return true/false
// in case of invalid inputs, it displays the error messages
if (input.validate()) {
// all input values are valid
// can retrieve values from input.values
console.log(input.values)
}
}