Issue
I want to make a buttonGroup that can be reused as a React component in React/Typescript. I know I can pass objects as props so that they can be mapped over. However currently my data objects take in functions that update the state of the active class of the buttons, I am a little confused as to how to do this? So I want to be able to do <ButtonGroup data={objects} />
but still maintain the active states of the buttons.
EDIT: Also as it is a button group, would need to have individual functions for each button too.
Any Idea’s? code so far:
component:
import React from 'react';
import {ButtonGroupState} from '../../shared/interfaces/ButtonGroup.interface';
const ButtonGroup: React.FunctionComponent = () => {
const objectsArray = [{
id: 1,
title: '1 element',
function(index: number) {
setActive({ ...active, activeObject: active.objects[index] })
}
}, {
id: 2,
title: '2 element',
function(index: number) {
setActive({ ...active, activeObject: active.objects[index] })
}
}, {
id: 3,
title: '3 element',
function(index: number) {
setActive({ ...active, activeObject: active.objects[index] })
}
}]
const [active, setActive] = React.useState<ButtonGroupState>({
activeObject: objectsArray[0],
objects: objectsArray
})
const toggleActiveStyles = (index: number) => {
if (active.objects[index] === active.activeObject) {
return "active"
} else {
return "not-active"
}
}
return (
<> <div className="btn-group">
{active.objects.map((inner, index) =>
<button type="button" className={toggleActiveStyles(index)} onClick={() => inner.function(index)} key={inner.id}>{inner.title}</button>
)}
</div>
</>
)
}
export default ButtonGroup;
interface
export interface ActiveObject {
id: number;
title: string;
function(index: number): void;
};
export interface ButtonGroupState {
activeObject: ActiveObject | null;
objects: ActiveObject[];
};
component usage so far:
<ButtonGroup />
Solution
Is there any reason each of your data objects has to carry a function around with it to update state? In your example, you’re onClick
handler passes the index to a function in your data object that finally passes the index to your state setter. Why not just cut out the middle man and update the state directly in your onClick
handler?
I think something like this should do the trick, given I’m understanding your question correctly:
const ButtonGroup: React.FunctionComponent<{ data: ActiveObject[] }> = ({ data: objects }) => {
const [activeIdx, setActiveIdx] = React.useState(0);
return (
<div className="btn-group">
{objects.map((inner, index) => (
<button
type="button"
className={activeIdx === index ? "active" : "not-active"}
onClick={() => {
setActiveIdx(index)
// You can pass the index, the event, or whatever you want here
inner.cb(index)
}}
key={inner.id}
>
{inner.title}
</button>
))}
</div>
);
};
Which can then be reused wherever you want, provided you supply the data
prop correctly:
const objects: ActiveObject[] = [
{
id: 1,
title: "1 element",
cb: (idx) => console.log(idx)
},
{
id: 2,
title: "2 element",
cb: (idx) => console.log(idx)
}
];
<ButtonGroup data={objects} />
Or, here’s a full working example on codesandbox.
Answered By – Sam Gomena
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0