问题
I am trying to dynamically render elements however when I try and splice the component from the array it does not get removed.
const [cardsInGrid, setCards] = React.useState([]);
const [showReactions, setShowReactions] = React.useState(false);
const onReactionsClick = () => {
setShowReactions(!showReactions);
};
useEffect(() => {
if (showReactions) {
setCards(cardsInGrid.concat(<Reactions />));
} else if (!showReactions) {
console.log(cardsInGrid);
var index = cardsInGrid.indexOf(<Reactions />);
console.log(index);
if (index > -1) {
setCards(cardsInGrid.splice(index, 1));
console.log(cardsInGrid);
}
}
}, [showReactions]);
The concat in the first if statement works but the console.log(index) returns -1 each time. is a component that I have.
EDIT: Array of components
const componentList = [
{ id: "tags", component: Tags },
{ id: "reactions", component: Reactions },
{ id: "emojistats", component: EmojiStats },
{ id: "filters", component: Filter },
];
How would I render these components if I have a predefined array?
回答1:
array::splice
splice
mutates the array in-place, but react state works by returning new objects and array references.
array::filter
Array filter can return the new array you need. Using the second parameter of the filter function you can filter all array elements' index that isn't equal to that of the one you want to remove from the array.
The
splice()
method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.
useEffect(() => {
if (showReactions) {
setCards(cardsInGrid.concat(<Reactions />));
} else if (!showReactions) {
console.log(cardsInGrid);
var index = cardsInGrid.indexOf(<Reactions />);
console.log(index);
if (index > -1) {
setCards(cardsInGrid.filter((_, i) => i !== index);
console.log(cardsInGrid);
}
}
}, [showReactions]);
EDIT: Using a component config array
/**
* External source of truth
* Could be global variable like this or passed as prop to rendering component
*/
const componentList = [
{ id: "tags", component: Tags },
{ id: "reactions", component: Reactions },
{ id: "emojiStats", component: EmojiStats },
{ id: "filters", component: Filters }
];
export default function App() {
// cardsInGrid displays current filtered component list
const [cardsInGrid, setCardsInGrid] = useState(componentList);
const [showReactions, setShowReactions] = React.useState(false);
useEffect(() => {
if (showReactions) {
// Filter Reactions component out
setCardsInGrid(componentList.filter(({ id }) => id !== "reactions"));
} else {
// Reset to full component list array
setCardsInGrid(componentList);
}
}, [showReactions]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div>
<button type="button" onClick={() => setShowReactions(r => !r)}>
{showReactions ? "Hide" : "Show"} Reactions
</button>
</div>
{cardsInGrid.map(({ id, component: Component }) => (
<Component key={id} />
))}
</div>
);
}
NOTE: If you need to filter by more than just a single component then a generalized solution could be a stored map of component config id's to filter by and using array::includes
const [filterBy, setFilterBy] = useState({});
// logic to handle adding/removing filters by name
// i.e. filterBy = { reactions: 'reactions', ...etc }
useEffect(() => {
const filters = Object.values(filterBy);
if (filters.length) {
// Filter components out if id matches anything in filter
setCardsInGrid(componentList.filter(({ id }) => filters.include(id)));
} else {
// Reset to full component list array
setCardsInGrid(componentList);
}
}, [filterBy]);
来源:https://stackoverflow.com/questions/61865549/component-not-being-recognised-in-array