Render Props in React

Learn how to use render props in React so that we can act on that data directly instead of having to carry many stateful variables in the app component.

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.

Press + to interact
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 ...