I want to show a div related to an object property that comes true or false. And I used a way but I'm not sure it's the best way or is it open the performance problems.
I'm checking that property in the loop that in return section for avoid extra array operation. But I think it will cause extra render.
The other option is checking that property in outside from return section. But this will be cause an extra array operation.
Which is the best way for me? I showed 2 different implementation below.
Option 1:
const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
const [showClearIcon, setShowClearIcon] = React.useState(false);
return (
<div className="radio-button-list">
{showClearIcon && <div className="clear-icon">clear</div>}
<ul>
{items.map(item => {
/* this is the area what I'm checking the property */
if (item.selected) {
setShowClearIcon(true);
}
return (
<li key={item.id}>
<label htmlFor={item.text} className="radio">
<span className="input">
<input type="radio" onClick={changeFilter} readOnly />
</span>
</label>
</li>
);
})}
</ul>
</div>
);
};
Option 2:
const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
const [showClearIcon, setShowClearIcon] = React.useState(false);
/* set in useEffect hook */
useEffect(() => {
if(items.some(item => item.selected)) {
setShowClearIcon(true);
}
}, [items]);
return (
<div className="radio-button-list">
{showClearIcon && <div className="clear-icon">clear</div>}
<ul>
{items.map(item => {
return (
<li key={item.id}>
<label htmlFor={item.text} className="radio">
<span className="input">
<input type="radio" onClick={changeFilter} readOnly />
</span>
</label>
</li>
);
})}
</ul>
</div>
);
};
It looks like showClearIcon doesn't need to be a state atom at all, but just a memoized value dependent on items.
const showClearIcon = React.useMemo(
() => items.some(item => item.selected),
[items],
);
Option 1 enqueues a state update in the render return, don't use it.
Use option 2 to correctly enqueue the update as a side effect. In React the render function is to be considered a pure function. Don't unconditionally enqueue state updates.
Regarding performance, iterating an array is O(n). Iterating an array twice is still O(n).
The showClearIcon "state" probably shouldn't be React state since it's easily derived from the items prop.
Identify the Minimal (but complete) Representation of UI State
Let’s go through each one and figure out which one is state. Ask three questions about each piece of data:
- Is it passed in from a parent via props? If so, it probably isn’t state.
- Does it remain unchanged over time? If so, it probably isn’t state.
- Can you compute it based on any other state or props in your component? If so, it isn’t state.
Because of this, just compute the showClearIcon value locally.
const showClearIcon = items.some(item => item.selected);
This can be memoized with the useMemo hook with dependency on items if necessary.
You can technically do it without useState and useEffect and without iterating over the array twice, check out the example below (it might not be neccessary but it's good to know that this is possible as well):
const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({
items,
changeFilter,
}) => {
const renderItems = () => {
let showClearIcon = false;
let markup = (
<ul>
{items.map((item) => {
if (item.selected) {
showClearIcon = true;
}
return (
<li key={item.id}>
<label htmlFor={item.text} className="radio">
<span className="input">
<input type="radio" onClick={changeFilter} readOnly />
</span>
</label>
</li>
);
})}
</ul>
);
return (
<>
{showClearIcon && <div className="clear-icon">clear</div>}
{markup}
</>
);
};
return <div className="radio-button-list">{renderItems()}</div>;
};
Simply created a function that generates the markup.