While reusability is good we need to maintain a balance between how much reusable code we need and how much separation of concern we need.
Few key points to keep in mind when architecting inside our current folder structure
Is the utility of the component being abstracted general purpose?
For example a a simple Tab component would be somewhere inside a tab folder inside common components, because it’s utility can be abstracted throughout the UI
whereas for components which are related specifically to admin or a startup side should be inside their respective folders inside the component
<aside> 💡 “Everything must be made as simple as possible. But not simpler.” - Albert Einstein
</aside>
It is a good idea to make components when we can have god separation of concerns inside one file and abstracting the component helps increase the overall readability and simplicity
Normal Data Fetching
import React from 'react';
import TextInput from './TextInput.js';
import SearchResult from './SearchResult.js';
const ENDPOINT = '/api/something';
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
const [
searchResults,
setSearchResults,
] = React.useState(null);
// idle | loading | success | error | empty
const [status, setStatus] = React.useState(
'idle'
);
async function handleSearch(event) {
event.preventDefault();
setStatus('loading');
const url = `${ENDPOINT}?searchTerm=${searchTerm}`;
const response = await fetch(url);
const json = await response.json();
if (json.ok) {
setSearchResults(json.results);
setStatus(
json.results.length > 0
? 'success'
: 'empty'
);
} else {
setStatus('error');
}
}
return (
<>
<header>
<form onSubmit={handleSearch}>
<TextInput
required={true}
label="Search"
placeholder="The Fifth Season"
value={searchTerm}
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button>Go!</button>
</form>
</header>
<main>
{status === 'idle' && (
<p>Welcome to book search!</p>
)}
{status === 'loading' && (
<p>Searching...</p>
)}
{status === 'error' && (
<p>Something went wrong!</p>
)}
{status === 'empty' && (
<p>No results</p>
)}
{status === 'success' && searchResults.length > 0 ?
(
<div className="search-results">
<h2>Search Results:</h2>
{searchResults?.map((result) => (
<SearchResult
key={result.isbn}
result={result}
/>
))
:
<p>No results found</p>
}
</div>
)}
</main>
</>
);
}
export default App;
Option 2 with valid separation of concern
import React from 'react';
import TextInput from './TextInput.js';
import SearchResult from './SearchResult.js';
const ENDPOINT =
'/api/something';
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
const [
searchResults,
setSearchResults,
] = React.useState(null);
// idle | loading | success | error
const [status, setStatus] = React.useState(
'idle'
);
async function handleSearch(event) {
event.preventDefault();
setStatus('loading');
const url = `${ENDPOINT}?searchTerm=${searchTerm}`;
const response = await fetch(url);
const json = await response.json();
if (json.ok) {
setSearchResults(json.results);
setStatus('success');
} else {
setStatus('error');
}
}
return (
<>
<header>
<form onSubmit={handleSearch}>
<TextInput
required={true}
label="Search"
placeholder="The Fifth Season"
value={searchTerm}
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button>Go!</button>
</form>
</header>
<main>
{status === 'idle' && (
<p>Welcome to book search!</p>
)}
{status === 'loading' && (
<p>Searching...</p>
)}
{status === 'error' && (
<p>Something went wrong!</p>
)}
{status === 'success' && (
<SearchResultList
searchResults={searchResults}
/>
)}
</main>
</>
);
}
function SearchResultList({ searchResults }) {
if (searchResults.length === 0) {
return <p>No results</p>;
}
return (
<div className="search-results">
<h2>Search Results:</h2>
{searchResults?.map((result) => (
<SearchResult
key={result.isbn}
result={result}
/>
))}
</div>
);
}
export default App;
For small components it may not look like the readability is getting improved here but in a larger codebase where there are multiple business logic happening inside a single file adding ternaries everywhere makes the code difficult to read over time
Search here is very context specific hence components like these must belong to the specific folder either admin or startup