Atomic design and object-oriented programming is the zeitgeist for today’s developers. But what and how can it be applied successfully for building products that are growing fast.
In another article about atomic design, we’ve discussed how to apply the fundamental principles and categorize components into atoms, molecules, or organisms. In this article, we dive deeper on how to implement this principle, design, and write code for these components.
One common oversight by most developers is to start building a solution before fully understanding the requirements and functionalities. The result is many iterations, work-arounds, and bug fixes that are effort intensive at a later stage and take away from the quality of the code.
Even when dealing with ambiguity, one must take the time to understand what provisions to build into the coding and engineering for ease of enhancements and iterations in the future.
For example, in a reminder app, we can build one atom and one organism.
1. TextField atom
2. AddEvent organism
Design the component structure
1. Decide whether to make it a stateful component or stateless component. The TextField is just supposed to take the input. Validations, errors, state management, etc. will be handled by the parent. Therefore, we’ll go with stateless.
2. Convert the requirements to props.
In the above code, there are a couple of extra props than earlier planned. This is fairly common. While it might be possible to think of all the scenarios all at once, it is good to have provisions for most of them.
There are no props for styling, because all the styling is handled by the theme, and will prevent other developers from overriding the uniform aesthetics of the application.
We always define the props first. This lays the path for the rest of the development. It also allows other developers, dependent on this component, to plan their development with ease, accelerating product development overall.
Implement the final code.
We can destructure the props easily, in accordance with our clients’ standards.
AddEvent organism is going to be a stateful component. This is our process for ensuring on-time delivery of the product.
This is going to be more complex than what we did for a stateless component.
First thing to decide is if the component is going to be the owner of this functionality or will it be controlled by its parent.
Next, answer this question: what is the impact of this component on the rest of the application?
Finally, identify how this component is going to be reused across the application.
We like to think of this component as a closed container, where it internally handles most of the processes. Few of its responsibilities are:
1. Render the correct UI
2. Handle the user input
3. Collate these data
4. Optional — Once the user clicks submit, then send the processed data back to the parent
In these scenarios, though the responsibility is high, most of it is abstracted and need not be exposed as props.
import React, { useCallback, useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Box from "@material-ui/core/Box";
import Grid from "@material-ui/core/Grid";
import Typography from "components/atoms/Typography";
import TextField from "components/atoms/TextField";
import Button from "components/atoms/Button";
const useStyle = makeStyles((theme) => ({}));
const AddEventBanner = (props) => {
const validate = { props };
const style = useStyle();
const [fieldToValidate, setFieldValidation] = useState({
title: false,
date: false,
type: false,
});
const [inputFieldValue, setFieldValue] = useState({
title: "",
description: "",
date: "",
type: "",
});
const validateField = useCallback(
(field) => {
const input = inputFieldValue[field];
const currentValidationState = fieldToValidate;
if (input === null || input === "") {
currentValidationState[field] = true;
} else {
currentValidationState[field] = false;
}
setFieldValidation({ ...currentValidationState });
return !currentValidationState[field];
},
[inputFieldValue, fieldToValidate, setFieldValidation]
);
const isValid = useCallback(() => {
let validationStatus = true;
Object.keys(fieldToValidate).map((key, value) => {
validationStatus = validationStatus && validateField(key);
console.log(validationStatus);
});
return validationStatus;
}, [fieldToValidate, validateField]);
const handleFieldChange = useCallback(
(field, value) => {
const currentValidationState = fieldToValidate;
currentValidationState[field] = false;
setFieldValidation({ ...currentValidationState });
const currentFieldValues = inputFieldValue;
currentFieldValues[field] = value;
setFieldValue({ ...currentFieldValues });
},
[fieldToValidate, inputFieldValue, setFieldValidation, setFieldValue]
);
const handleSubmit = useCallback(() => {
if (!props.validate || (props.validate && isValid())) {
props.onSubmit(inputFieldValue);
}
}, [isValid]);
return (
<Box component={Container}>
<Grid container spacing={2}>
<Grid item>
<Typography variant="header">Add New Event</Typography>
</Grid>
<Grid item xs={12}>
<TextField
required
label="Title"
onChange={(event) => handleFieldChange("title", event.target.value)}
error={fieldToValidate.title}
onBlur={() => validate && validateField("title")}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="Description"
onChange={(event) =>
handleFieldChange("description", event.target.value)
}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Date and Time"
onChange={(event) => handleFieldChange("date", event.target.value)}
type="datetime-local"
error={fieldToValidate.date}
onBlur={() => validate && validateField("date")}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Event Type"
onChange={(event) => handleFieldChange("type", event.target.value)}
error={fieldToValidate.type}
onBlur={() => validate && validateField("type")}
/>
</Grid>
<Grid item container xs={12} justify="center">
<Button title="Add Event" onClick={handleSubmit} />
</Grid>
</Grid>
</Box>
);
};
AddEventBanner.propTypes = {
validate: PropTypes.bool,
onSubmit: PropTypes.func,
};
AddEventBanner.defaultProps = {
validate: false,
};
export default AddEventBanner;
AddEvent.js hosted with ❤ by GitHub. View raw code here.
You can take a peek at my reference code for this post and project here.
An earlier version of this blog was published on Medium by the author.
Atomic design and object-oriented programming is the zeitgeist for today’s developers. But what and how can it be applied successfully for building products that are growing fast.
In another article about atomic design, we’ve discussed how to apply the fundamental principles and categorize components into atoms, molecules, or organisms. In this article, we dive deeper on how to implement this principle, design, and write code for these components.
One common oversight by most developers is to start building a solution before fully understanding the requirements and functionalities. The result is many iterations, work-arounds, and bug fixes that are effort intensive at a later stage and take away from the quality of the code.
Even when dealing with ambiguity, one must take the time to understand what provisions to build into the coding and engineering for ease of enhancements and iterations in the future.
For example, in a reminder app, we can build one atom and one organism.
1. TextField atom
2. AddEvent organism
Design the component structure
1. Decide whether to make it a stateful component or stateless component. The TextField is just supposed to take the input. Validations, errors, state management, etc. will be handled by the parent. Therefore, we’ll go with stateless.
2. Convert the requirements to props.
In the above code, there are a couple of extra props than earlier planned. This is fairly common. While it might be possible to think of all the scenarios all at once, it is good to have provisions for most of them.
There are no props for styling, because all the styling is handled by the theme, and will prevent other developers from overriding the uniform aesthetics of the application.
We always define the props first. This lays the path for the rest of the development. It also allows other developers, dependent on this component, to plan their development with ease, accelerating product development overall.
Implement the final code.
We can destructure the props easily, in accordance with our clients’ standards.
AddEvent organism is going to be a stateful component. This is our process for ensuring on-time delivery of the product.
This is going to be more complex than what we did for a stateless component.
First thing to decide is if the component is going to be the owner of this functionality or will it be controlled by its parent.
Next, answer this question: what is the impact of this component on the rest of the application?
Finally, identify how this component is going to be reused across the application.
We like to think of this component as a closed container, where it internally handles most of the processes. Few of its responsibilities are:
1. Render the correct UI
2. Handle the user input
3. Collate these data
4. Optional — Once the user clicks submit, then send the processed data back to the parent
In these scenarios, though the responsibility is high, most of it is abstracted and need not be exposed as props.
import React, { useCallback, useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Box from "@material-ui/core/Box";
import Grid from "@material-ui/core/Grid";
import Typography from "components/atoms/Typography";
import TextField from "components/atoms/TextField";
import Button from "components/atoms/Button";
const useStyle = makeStyles((theme) => ({}));
const AddEventBanner = (props) => {
const validate = { props };
const style = useStyle();
const [fieldToValidate, setFieldValidation] = useState({
title: false,
date: false,
type: false,
});
const [inputFieldValue, setFieldValue] = useState({
title: "",
description: "",
date: "",
type: "",
});
const validateField = useCallback(
(field) => {
const input = inputFieldValue[field];
const currentValidationState = fieldToValidate;
if (input === null || input === "") {
currentValidationState[field] = true;
} else {
currentValidationState[field] = false;
}
setFieldValidation({ ...currentValidationState });
return !currentValidationState[field];
},
[inputFieldValue, fieldToValidate, setFieldValidation]
);
const isValid = useCallback(() => {
let validationStatus = true;
Object.keys(fieldToValidate).map((key, value) => {
validationStatus = validationStatus && validateField(key);
console.log(validationStatus);
});
return validationStatus;
}, [fieldToValidate, validateField]);
const handleFieldChange = useCallback(
(field, value) => {
const currentValidationState = fieldToValidate;
currentValidationState[field] = false;
setFieldValidation({ ...currentValidationState });
const currentFieldValues = inputFieldValue;
currentFieldValues[field] = value;
setFieldValue({ ...currentFieldValues });
},
[fieldToValidate, inputFieldValue, setFieldValidation, setFieldValue]
);
const handleSubmit = useCallback(() => {
if (!props.validate || (props.validate && isValid())) {
props.onSubmit(inputFieldValue);
}
}, [isValid]);
return (
<Box component={Container}>
<Grid container spacing={2}>
<Grid item>
<Typography variant="header">Add New Event</Typography>
</Grid>
<Grid item xs={12}>
<TextField
required
label="Title"
onChange={(event) => handleFieldChange("title", event.target.value)}
error={fieldToValidate.title}
onBlur={() => validate && validateField("title")}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="Description"
onChange={(event) =>
handleFieldChange("description", event.target.value)
}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Date and Time"
onChange={(event) => handleFieldChange("date", event.target.value)}
type="datetime-local"
error={fieldToValidate.date}
onBlur={() => validate && validateField("date")}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Event Type"
onChange={(event) => handleFieldChange("type", event.target.value)}
error={fieldToValidate.type}
onBlur={() => validate && validateField("type")}
/>
</Grid>
<Grid item container xs={12} justify="center">
<Button title="Add Event" onClick={handleSubmit} />
</Grid>
</Grid>
</Box>
);
};
AddEventBanner.propTypes = {
validate: PropTypes.bool,
onSubmit: PropTypes.func,
};
AddEventBanner.defaultProps = {
validate: false,
};
export default AddEventBanner;
AddEvent.js hosted with ❤ by GitHub. View raw code here.
You can take a peek at my reference code for this post and project here.
An earlier version of this blog was published on Medium by the author.
Atomic design and object-oriented programming is the zeitgeist for today’s developers. But what and how can it be applied successfully for building products that are growing fast.
In another article about atomic design, we’ve discussed how to apply the fundamental principles and categorize components into atoms, molecules, or organisms. In this article, we dive deeper on how to implement this principle, design, and write code for these components.
One common oversight by most developers is to start building a solution before fully understanding the requirements and functionalities. The result is many iterations, work-arounds, and bug fixes that are effort intensive at a later stage and take away from the quality of the code.
Even when dealing with ambiguity, one must take the time to understand what provisions to build into the coding and engineering for ease of enhancements and iterations in the future.
For example, in a reminder app, we can build one atom and one organism.
1. TextField atom
2. AddEvent organism
Design the component structure
1. Decide whether to make it a stateful component or stateless component. The TextField is just supposed to take the input. Validations, errors, state management, etc. will be handled by the parent. Therefore, we’ll go with stateless.
2. Convert the requirements to props.
In the above code, there are a couple of extra props than earlier planned. This is fairly common. While it might be possible to think of all the scenarios all at once, it is good to have provisions for most of them.
There are no props for styling, because all the styling is handled by the theme, and will prevent other developers from overriding the uniform aesthetics of the application.
We always define the props first. This lays the path for the rest of the development. It also allows other developers, dependent on this component, to plan their development with ease, accelerating product development overall.
Implement the final code.
We can destructure the props easily, in accordance with our clients’ standards.
AddEvent organism is going to be a stateful component. This is our process for ensuring on-time delivery of the product.
This is going to be more complex than what we did for a stateless component.
First thing to decide is if the component is going to be the owner of this functionality or will it be controlled by its parent.
Next, answer this question: what is the impact of this component on the rest of the application?
Finally, identify how this component is going to be reused across the application.
We like to think of this component as a closed container, where it internally handles most of the processes. Few of its responsibilities are:
1. Render the correct UI
2. Handle the user input
3. Collate these data
4. Optional — Once the user clicks submit, then send the processed data back to the parent
In these scenarios, though the responsibility is high, most of it is abstracted and need not be exposed as props.
import React, { useCallback, useState } from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Box from "@material-ui/core/Box";
import Grid from "@material-ui/core/Grid";
import Typography from "components/atoms/Typography";
import TextField from "components/atoms/TextField";
import Button from "components/atoms/Button";
const useStyle = makeStyles((theme) => ({}));
const AddEventBanner = (props) => {
const validate = { props };
const style = useStyle();
const [fieldToValidate, setFieldValidation] = useState({
title: false,
date: false,
type: false,
});
const [inputFieldValue, setFieldValue] = useState({
title: "",
description: "",
date: "",
type: "",
});
const validateField = useCallback(
(field) => {
const input = inputFieldValue[field];
const currentValidationState = fieldToValidate;
if (input === null || input === "") {
currentValidationState[field] = true;
} else {
currentValidationState[field] = false;
}
setFieldValidation({ ...currentValidationState });
return !currentValidationState[field];
},
[inputFieldValue, fieldToValidate, setFieldValidation]
);
const isValid = useCallback(() => {
let validationStatus = true;
Object.keys(fieldToValidate).map((key, value) => {
validationStatus = validationStatus && validateField(key);
console.log(validationStatus);
});
return validationStatus;
}, [fieldToValidate, validateField]);
const handleFieldChange = useCallback(
(field, value) => {
const currentValidationState = fieldToValidate;
currentValidationState[field] = false;
setFieldValidation({ ...currentValidationState });
const currentFieldValues = inputFieldValue;
currentFieldValues[field] = value;
setFieldValue({ ...currentFieldValues });
},
[fieldToValidate, inputFieldValue, setFieldValidation, setFieldValue]
);
const handleSubmit = useCallback(() => {
if (!props.validate || (props.validate && isValid())) {
props.onSubmit(inputFieldValue);
}
}, [isValid]);
return (
<Box component={Container}>
<Grid container spacing={2}>
<Grid item>
<Typography variant="header">Add New Event</Typography>
</Grid>
<Grid item xs={12}>
<TextField
required
label="Title"
onChange={(event) => handleFieldChange("title", event.target.value)}
error={fieldToValidate.title}
onBlur={() => validate && validateField("title")}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="Description"
onChange={(event) =>
handleFieldChange("description", event.target.value)
}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Date and Time"
onChange={(event) => handleFieldChange("date", event.target.value)}
type="datetime-local"
error={fieldToValidate.date}
onBlur={() => validate && validateField("date")}
/>
</Grid>
<Grid item xs={6}>
<TextField
required
label="Event Type"
onChange={(event) => handleFieldChange("type", event.target.value)}
error={fieldToValidate.type}
onBlur={() => validate && validateField("type")}
/>
</Grid>
<Grid item container xs={12} justify="center">
<Button title="Add Event" onClick={handleSubmit} />
</Grid>
</Grid>
</Box>
);
};
AddEventBanner.propTypes = {
validate: PropTypes.bool,
onSubmit: PropTypes.func,
};
AddEventBanner.defaultProps = {
validate: false,
};
export default AddEventBanner;
AddEvent.js hosted with ❤ by GitHub. View raw code here.
You can take a peek at my reference code for this post and project here.
An earlier version of this blog was published on Medium by the author.