Composite react elements
Building a React component as a re-usable unit, you can achieve this by many ways.
If you want to make it composite, like a component works irrespective or UI or Children we are passing to it: FaaC, Function as a Children is a best way to do it.
Let's Build a SearchBar
if you think of building a SearchBar component in react, you would be thinking something like this:
import React, { useState } from 'react';
function SearchBar() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/search?q=${searchTerm}`);
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const data = await response.json();
setResults(data);
setError(null);
} catch (error) {
console.error('Error fetching data:', error);
setError('Failed to fetch data. Please try again later.');
setResults([]);
}
};
const handleSearch = async () => {
if (searchTerm.trim() !== '') {
await fetchData();
}
};
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Enter search term..."
/>
<button onClick={handleSearch}>Search</button>
{error && <p>{error}</p>}
<div>
{results.map((result) => (
<div key={result.id}>
<h3>{result.title}</h3>
<p>{result.description}</p>
</div>
))}
</div>
</div>
);
}
export default SearchBar;
If you see the above example, the Result UI is coupled with SearchBar Component.
Although, if you create a SearchBar like this, you have to stick with same UI for all places you gonna use this component.
Here comes FaaC in action
<div>
{results.map((result) => (
<div key={result.id}>
<h3>{result.title}</h3>
<p>{result.description}</p>
</div>
))}
</div>
We don't need this Actually, we can use the SearchBar as a parent Component and pass Children as a Function rather passing JSX Children.
Define Component to accept children
function SearchBar(props) {...}
or
function SearchBar({children) {...}
We know every SearchBar going to have input field, but result display could be different (overlay, list, fullPage) etc...
In the component lets keep the input field with all the state, except the result UI, in place of result UI, lets call the children function.
{
typeof children === "function" ?
children() : //works as Function
children // works as a JX
}
But, when you are calling the function which has been passed as a Children, you can pass state values as well.
function SearchBar({children}) {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/search?q=${searchTerm}`);
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const data = await response.json();
setResults(data);
setError(null);
} catch (error) {
console.error('Error fetching data:', error);
setError('Failed to fetch data. Please try again later.');
setResults([]);
}
};
const handleSearch = async () => {
if (searchTerm.trim() !== '') {
await fetchData();
}
};
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Enter search term..."
/>
<button onClick={handleSearch}>Search</button>
{error && <p>{error}</p>}
//FaaC
{
typeof children === "function" ?
children({searchTerm,results}) : //passing state as arguments
children
}
</div>
);
}
export default SearchBar;
Now, in the actual Component usage, you can pass a funtion rather than JSX element.
<SearchBar>
{
({results,searchTerm})=>{
return results.map((result) => (
<div key={result.id}>
<h3>{result.title}</h3>
<p>{result.description}</p>
</div>
))
}
}
</SearchBar>
Now in the above code, your core functionality of searching and fetching results stays in SearchBar but but but... passing a function as a Children, giving you flexibility to change UI as per the requirement, you don't need to redefine the business logic or add conditions to display different UIs.
It is the best way to create composite elements in React.