Search⌘ K
AI Features

Render Props in React

Explore how to implement render props in React to build flexible and reusable components for searching, sorting, and filtering data. Understand how to manage state and props effectively while handling dynamic data input and improving code clarity and type safety using TypeScript generics.

So far, all of our application examples have a single functionality, either searching, sorting, or filtering. But what if we want to have search, sort, and filter abilities acting on our data in combination? Unfortunately, if we combine the code for search, sorting, and filtering, our App.tsx becomes bloated and hard to understand.

TypeScript 3.3.4
function App() {
const [query, setQuery] = useState<string>("");
const [showPeople, setShowPeople] = useState<boolean>(false);
const [widgetSortProperty, setWidgetSortProperty] = useState<
ISorter<IWidget>
>({ property: "title", isDescending: true });
const [widgetFilterProperties, setWidgetFilterProperties] = useState<
Array<IFilter<IWidget>>
>([]);
const [peopleSortProperty, setPeopleSortProperty] = useState<
ISorter<IPerson>
>({ property: "firstName", isDescending: true });
const [peopleFilterProperties, setPeopleFilterProperties] = useState<
Array<IFilter<IPerson>>
>([]);
const buttonText = showPeople ? "Show widgets" : "Show people";
return (
<>
<button
className="btn btn-primary"
onClick={() => setShowPeople(!showPeople)}
>
{buttonText}
</button>
<SearchInput
setSearchQuery={(query) => {
setQuery(query);
}}
/>
{!showPeople && (
<>
<h2>Widgets:</h2>
<Sorters
setProperty={(propertyType) => {
setWidgetSortProperty(propertyType);
}}
object={widgets[0]}
/>
<br />
<Filters
object={widgets[0]}
properties={widgetFilterProperties}
onChangeFilter={(property) => {
const propertyMatch = widgetFilterProperties.some(
(widgetFilterProperty) =>
widgetFilterProperty.property === property.property
);
const fullMatch = widgetFilterProperties.some(
(widgetFilterProperty) =>
widgetFilterProperty.property === property.property &&
widgetFilterProperty.isTruthySelected ===
property.isTruthySelected
);
if (fullMatch) {
setWidgetFilterProperties(
widgetFilterProperties.filter(
(widgetFilterProperty) =>
widgetFilterProperty.property !== property.property
)
);
} else if (propertyMatch) {
setWidgetFilterProperties([
...widgetFilterProperties.filter(
(widgetFilterProperty) =>
widgetFilterProperty.property !== property.property
),
property,
]);
} else {
setWidgetFilterProperties([
...widgetFilterProperties,
property,
]);
}
}}
/>
{widgets
.filter((widget) =>
genericSearch(widget, ["title", "description"], query, false)
)
.filter((widget) => genericFilter(widget, widgetFilterProperties))
.sort((a, b) => genericSort(a, b, widgetSortProperty))
.map((widget) => {
return <WidgetRenderer {...widget} />;
})}
</>
)}
{showPeople && (
<>
<h2>People:</h2>
<Sorters
setProperty={(propertyType) => {
setPeopleSortProperty(propertyType);
}}
object={people[0]}
/>
<Filters
object={people[0]}
properties={peopleFilterProperties}
onChangeFilter={(property) => {
const propertyMatch = peopleFilterProperties.some(
(peopleFilterProperty) =>
peopleFilterProperty.property === property.property
);
const fullMatch = peopleFilterProperties.some(
(peopleFilterProperty) =>
peopleFilterProperty.property === property.property &&
peopleFilterProperty.isTruthySelected ===
property.isTruthySelected
);
if (fullMatch) {
setPeopleFilterProperties(
peopleFilterProperties.filter(
(peopleFilterProperty) =>
peopleFilterProperty.property !== property.property
)
);
} else if (propertyMatch) {
setPeopleFilterProperties([
...peopleFilterProperties.filter(
(peopleFilterProperty) =>
peopleFilterProperty.property !== property.property
),
property,
]);
} else {
setPeopleFilterProperties([
...peopleFilterProperties,
property,
]);
}}}
/>
{people
.filter((person) =>
genericSearch(
person,
["firstName", "lastName", "eyeColor"],
query,
false
)
)
.filter((person) => genericFilter(person, peopleFilterProperties))
.sort((a, b) => genericSort(a, b, peopleSortProperty))
.map((person) => {
return <PeopleRenderer {...person} />;
})}
</>
)}
</>
);
}
export default App;

In addition to being difficult to understand, there is another problem with the code we’ve written so far—at least with using the object prop for sorting and filtering. In our examples, widgets and people are static lists. But, when we load this data in an API, we can’t guarantee we’ll always have an entity within widget[0] and people[0]. Either could be an empty array, or depending on the API conventions, null or undefined entirely. We should design our ...