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>
);
};
CodePudding user response:
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],
);
CodePudding user response:
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)
.
Suggestion
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.
CodePudding user response:
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.
CodePudding user response:
So, what you can do if you want to purely look at performance, is install the react dev tools chrome extension, then in your test server (for example localhost:3000) you can use the profiler tab in chrome's f12 menu to check which operation takes more memory!
this is something you can use for all optimization questions
CodePudding user response:
If your goal is to render html components, option 2 (useEffect) should be used, by keeping in mind that only the required dependencies are included in the dependency array (this is to avoid re-render and performance related issues). In your case, option 2 is correct, and its performance will be tuned by React itself.
CodePudding user response:
Option 2 is better approach, but Option 2 you can also have little better coding as below to make code more better and simpler
const RadioButtonList: FunctionComponent<RadioButtonListProps> = ({ items, changeFilter }) => {
// as suggested by AKX
const showClearIcon = React.useMemo(
() => items.some(item => item.selected),
[items],
);
/* set in useEffect hook */
useEffect(() => {
if(items.some(item => item.selected)) {
setShowClearIcon(true);
}
}, [items]);
// for displaying row items
const generateRow = ({id, text}) => {
return (
<li key={item.id}>
<label htmlFor={item.text} className="radio">
<span className="input">
<input type="radio" onClick={changeFilter} readOnly />
</span>
</label>
</li>
);
}
return (
<div className="radio-button-list">
{showClearIcon && <div className="clear-icon">clear</div>}
<ul>
{items.map(item => generateRow(item))}
</ul>
</div>
);
};