How can I make handleclick operation unique for each icon? For example when click to plus icon, color of all changes to green. But I want only plus icon to change into green.
const [isActive, setIsActive] = useState(false);
const handleClick = () => {
setIsActive((current) => !current);
};
return (
<div className="list-icons">
<FaPlus
className="plus-icon"
style={{
color: isActive ? "green" : "",
}}
onClick={handleClick}
/>
<FaCheck
className="check-icon"
style={{
color: isActive ? "green" : "",
}}
onClick={handleClick}
/>
<FaHeart
className="heart-icon"
style={{
color: isActive ? "green" : "",
}}
onClick={handleClick}
/>
</div>
)
The best way to do that is that you should create an array of object for your icons.
For example
const [icons, setIcons] = useState([
{
id: 1,
icon: FaPlus,
className:"plus-icon",
isActive: false,
},
{
id: 2,
icon: FaCheck,
className:"check-icon",
isActive: false,
},
{
id: 3,
icon: FaHeart,
className:"heart-icon",
isActive: false,
},
]);
const handleClick = (id: number) => {
const newIcons = icons.map((icon) => {
if(icon.id === id) {
return {
...icon,
isActive: !icon.isActive,
}
}
return icon;
});
setIcons(newIcons);
};
return (
<div className="list-icons">
{icons.map((icon) => {
const Icon = icon.icon
return (
(
<Icon
className={icon.className}
style={{
color: icon.isActive ? "green" : "",
}}
onClick={() => handleClick(icon.id}
/>
)
})
</div>
)
You should use an array of boolean:
const [isActive, setIsActive] = useState([false, false, false]);
const handleClick = (index) => {
setIsActive((current) => {
current[index] = !current[index];
return current;
});
};
return (
<div className="list-icons">
<FaPlus
className="plus-icon"
style={{
color: isActive[0] ? "green" : "",
}}
onClick={() => handleClick(0)}
/>
<FaCheck
className="check-icon"
style={{
color: isActive[1] ? "green" : "",
}}
onClick={() => handleClick(1)}
/>
<FaHeart
className="heart-icon"
style={{
color: isActive[2] ? "green" : "",
}}
onClick={() => handleClick(2)}
/>
</div>
)
const icons = ['plus', 'check', 'heart'];
const components = [<FaPlus />, <FaCheck />, <FaHeart />];
const [activeIcons, setActiveIcons] = useState(icons.map(() => false));
const onActiveToggle = (index) => {
setActiveIcons(prev => {
prev[index] = !prev[index];
return prev;
});
}
const iconProps = useMemo(() => {
return icons.map((icon, index) => ({
className: `${icon}-icon`,
style: {{ color: activeIcons[index] ? 'green': 'black' }},
onClick: () => onActiveToggle(index)
})
}, [activeIcons]);
return (
<>
{components.map((Component, index) => (
<Component {...iconProps[index]}/>
))}
</>
);
Related
I'm struggling with Formik when using custom component for <Field> and my own onChange for this input.
The problem is that whenever I resetForm() (it's now set inside AutoSubmitHandler just for tests), the values are set well, it means that I get { q: ''} after each reset, but the value inside input itself stays filled.
Reproduction steps:
I type something in the input - "foo"
I get console.logged the values: { q: "foo" }
The resetForm() triggers and I get { q: ''}
But in the input I can still see "foo"
It looks like this:
const CustomDarkTextField = styled(TextField)<TextFieldProps>(() => ({
width: '50%',
marginBottom: '10px',
bgcolor: '#5C677D',
'& .MuiInputBase-root': {
bgcolor: '#5C677D',
},
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiFormLabel-root': {
color: 'white',
fontWeight: 600,
},
'& .MuiFormLabel-root.Mui-focused': {
color: 'white',
fontWeight: 600,
},
'& .MuiFormHelperText-root.Mui-error': {
color: '#F77F00',
},
'& .MuiOutlinedInput-root.Mui-focused': {
'& > fieldset': { border: '0' },
},
'& .MuiOutlinedInput-root.Mui-error': {
'& > fieldset': { borderColor: '#F77F00' },
},
'& .MuiTypography-root': {
color: 'white',
fontWeight: 600,
},
}));
export const FilterCustomDarkTextField = ({ field, label, onChange, setFieldValue }: FilterTextFieldProps) => {
return (<CustomDarkTextField name={field?.name} label={label} onChange={e => onChange(e, setFieldValue)} />);
};
and my filter form component:
return (
<div>
<Formik
initialValues={defaultFilterValues}
onSubmit={(values) => {
handleSubmit(values);
}}
enableReinitialize={true}
>
{({ setFieldValue, values }) => (
<Form className='form flex flex-col gap-4 mx-auto my-8 w-full items-center'>
<h2 className="self-start">Data</h2>
{Object.keys(filterFields).map(field => (
<>
<Field
key={field}
{...filterFields[field as keyof typeof filterFields]}
setFieldValue={setFieldValue}
/>
</>
))}
<AutoSubmitHandler />
</Form>
)}
</Formik>
</div>
);
AutoSubmit:
const AutoSubmitHandler = () => {
const { values, resetForm } = useFormikContext();
useEffect(() => {
console.log(values);
resetForm();
}, [values]);
return null;
};
fields:
const FILTER_FIELDS = {
football: {
q: {
name: 'q',
label: 'name/surname',
component: FilterCustomDarkTextField,
onChange: debouncedHandleChange,
},
},
};
onChange method:
export const debouncedHandleChange = _.debounce((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, setFieldValue) => {
const { name, value } = e.target;
setFieldValue(name, value);
}, 1500);
The code below I have provided , one example opens the dialog. And the dialog has add functionality which is the addEmail where in you can add multiple fields which is inside the dialog.
I wanna know is that when I close the dialog using the onClick={handleClose} it should reset the dialog, fields that I added should not show after I close since I did not save it.
So when I click cancel it should reset the state.
Thanks for any idea.
for example here I added fields when I close this and open again these field should not show cause it should reset when I close.
#interface.ts
export type EditPropertiesProps = {
open: boolean;
handleClose: () => void;
selectedRow: IRegional
};
#Code snippet - main page --- this calls and opens the dialog
const handleClose = () => {
console.log('here')
setOpen(false);
};
<EditProperties open={open} handleClose={handleClose} selectedRow={selectedRow} />
#EditProperties ts code
export const RegionalListData: IRegionalList[] = [
{
id: 4,
name: "Associate Director of Construction Ops",
column: "associateDirectorofConstructionOps",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 5,
name: "CAM Manager",
column: "camManager",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 6,
name: "CAO-Chief Administrative Officer",
column: "caoChiefAdministrativeOfficer",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
];
type InitialReqPaylod = {
accountId: number;
regionalRoleUserDto: IRegional;
};
type IData = {
regionName: string;
marketName: string;
subRegionName: string;
};
type IEmail = {
emailAddress: string;
firstName: string;
id: number;
lastName: string;
};
const EditProperties: FC<EditPropertiesProps> = ({
open,
handleClose,
selectedRow,
}) => {
const dispatch = useAppDispatch();
const [isEmailOpen, setOpenEmail] = useState(false);
const [fetching, setFetching] = useState(false);
const [RegionalList, setRegionalList] = useState<IRegionalList[]>(
RegionalListData
);
const [data, setData] = useState<IData>({
regionName: "",
marketName: "",
subRegionName: "",
});
const [regionalId, setRegionalId] = useState<number | null>(null);
const [emailCurrentIndex, setEmailCurrentIndex] = useState<number | null>(
null
);
const [selectedEmailId, setSelectedEmailId] = useState<number | null>(null);
const { isSuccess } = useAppSelector((state) => state.yardUser);
const { isSaveSuccess } = useAppSelector((state) => state.region);
const email = useAppSelector((state) => state.yardUser);
const [emailOptions, setEmailOptions] = useState<IEmail[]>([]);
const emailList = email.data ? email.data.data : [];
useEffect(() => {
if (selectedRow) {
setData({
regionName: selectedRow["regionName"],
marketName: selectedRow["marketName"],
subRegionName: selectedRow["subRegionName"],
});
let regional = [...RegionalList];
for (const k in selectedRow) {
regional.map((prop: IRegionalList) => {
if (prop.column === k) {
prop.emails = selectedRow[k] ? selectedRow[k] : [];
}
});
}
setRegionalList(regional);
}
}, [selectedRow]);
const [maxWidth, setMaxWidth] = React.useState<DialogProps["maxWidth"]>("md");
const fetchEmailResult = React.useMemo(
() =>
throttle(
(event: any, callback: (results: IEmail[]) => void) => {
const payload: IYardUserRequestPayload | InitialReqPaylod = {
accountId: 1,
searchString: event.target.value,
};
fetch(
`https://jsonplaceholder.typicode.com/users?email=${event.target.value}`
)
.then((res) => res.json())
.then((res) => res.data ? callback(res.data.slice(0, 10)) : callback([]))
},
200
),
[]
);
const emailOnChange = (event: any, regionalId: number, index: number, emailId: number) => {
setRegionalId(regionalId);
setEmailCurrentIndex(index);
setSelectedEmailId(emailId);
fetchEmailResult(event,(results: IEmail[]) => {
console.log('results' , results)
if (results.length) setEmailOptions(results);
});
};
useEffect(() => {
if (isSaveSuccess) {
handleClose();
}
}, [isSaveSuccess]);
useEffect(() => {
if (isSuccess) {
setFetching(false);
}
}, [isSuccess]);
const addEmail = (id: number) => {
setRegionalList((list) =>
list.map((item) => {
if (item.id === id) {
return {
...item,
emails: [
...item.emails,
{
emailAddress: "",
firstName: "",
lastName: "",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
};
}
return item;
})
);
};
const deleteEmail = (email: IEmail, regionId: number) => {
const regionalListCopy = [...RegionalList].map((prop: IRegionalList) => {
if (prop.id === regionId) {
return {
...prop,
emails: prop.emails.filter((prop) => prop.id !== email.id),
};
}
return { ...prop };
});
setRegionalList(regionalListCopy);
};
const setOnChangeOption = (email) => {
setSelectedEmailId(null);
setRegionalList((list) =>
list.map((item) => {
if (item.id === regionalId) {
return {
...item,
emails: [
...item.emails.map((prop) => {
return {
...prop,
...email,
};
}),
],
};
}
return item;
})
);
};
const EmailItem = ({ email, mIndex, prop }) => (
<>
<div style={{ display: "block" }} key={email.id}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 15
}}
>
<Autocomplete
options={emailOptions}
getOptionLabel={(option: IEmail) => option.emailAddress}
onInputChange={($event) => emailOnChange($event, prop.id, mIndex, email.id)}
onChange={($event, value) => setOnChangeOption(value)}
fullWidth
open={email.id === selectedEmailId}
renderInput={(params) => (
<TextField size="small" {...params} variant="standard" />
)}
renderOption={(props, option) => {
return (
<Box component="li" {...props}>
{option.emailAddress}
</Box>
);
}}
/>
<DeleteIcon
style={{ color: "red", cursor: "pointer" }}
onClick={() => deleteEmail(email, prop.id)}
/>
</div>
<div
style={{
fontSize: ".8em",
display: "flex",
justifyContent: "space-between",
}}
>
<span style={{ paddingTop: 5 }}>
Email : {email.emailAddress}
Full Name: {email.firstName} {email.lastName}
</span>
{/* <span style={{ paddingRight : 40 }}>{fetching ? "Fetching...." : null}</span> */}
</div>
</div>
</>
);
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Edit</DialogTitle>
<DialogContent>
<Card sx={{ minWidth: 275 }} style={{ padding: 20 }}>
<div>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Region (Division)"
variant="filled"
value={data.regionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Sub-Region (Sub-Division)"
variant="filled"
value={data.subRegionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Market</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Market"
variant="filled"
value={data.marketName}
/>
</FormControl>
</div>
</Card>
{RegionalList.map((prop: IRegionalList, index: number) => (
<Card
sx={{ minWidth: 275 }}
style={{ overflow: "visible", padding: 20, marginTop: 20 }}
key={prop.id}
>
<div style={{ display: "flex", alignItems: "center" }}>
{prop.name}*{" "}
<AddIcon
style={{ marginLeft: 5, cursor: "pointer" }}
onClick={() => addEmail(prop.id)}
/>
</div>
<Divider style={{ marginTop: 10 }} />
{prop.emails.map((email: IEmail, mIndex: number) => (
<EmailItem
key={email.id}
prop={prop}
email={email}
mIndex={mIndex}
/>
))}
</Card>
))}
</DialogContent>
<DialogActions
style={{ marginTop: "20px", marginRight: "20px", marginBottom: "20px" }}
>
<Button onClick={handleClose}>Cancel</Button>
<Button variant="contained" onClick={() => saveChanges()} autoFocus>
Save Changes
</Button>
</DialogActions>
</Dialog>
);
};
export default EditProperties;
You need just reset all used states as values of form when clicking handleClose, my suggestion would be to use just one object state for form values.
Example:
const onClose = () => {
handleClose();
setRegionalList(RegionalListData);
}
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
I'm working on Register Form using MaterialUI stepper. Each stepper render a component:
const steps = ['Basic informations', 'Profile', 'Review your registration']
And there's a Register button at the end step, click on it should handleSubmit the form data.
I tried to make this concept using this code below, but it didn't work. Can you fix it for me please?
const steps = ['Basic informations', 'Profile', 'Review your registration']
export default function Register() {
const [activeStep, setActiveStep] = React.useState(0)
const [stateRegister, setStateRegister] = useState({})
const [stateProfile, setStateProfile] = useState({})
function getStepContent(step) {
switch (step) {
case 0:
return <BasicForm stateOfRegister={stateOfRegister} />
case 1:
return <ProfileForm stateOfProfile={stateOfProfile} />
case 2:
return <Review stateRegister={stateRegister} stateProfile={stateProfile} />
default:
throw new Error('Unknown step')
}
}
const stateOfRegister = (props) => {
setStateRegister(props)
}
const stateOfProfile = (props) => {
setStateProfile(props)
}
const handleNext = () => {
setActiveStep(activeStep + 1)
}
const handleBack = () => {
setActiveStep(activeStep - 1)
}
const handleNavigate = () => {
navigate('/')
}
const handleSubmit = async () => {
const user = { ...stateRegister, ...stateProfile, role: 'user', uid: 'azeq' }
await addUser(user)
}
return (
<React.Fragment>
{getStepContent(activeStep)}
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
{activeStep !== 0 && (
<>
<Button onClick={handleBack} sx={{ mt: 3, ml: 1 }}>
Back
</Button>
</>
)}
<Button
variant="contained"
onClick={() => {
handleNext()
{
activeStep === 3 && handleSubmit() //this is my try
}
}}
sx={{ mt: 3, ml: 1 }}
>
{activeStep === steps.length - 1 ? 'Register' : 'Next'}
</Button>
</Box>
</React.Fragment>
)
}
If you change the click handler on the button it might behave the way that you would like.
const onNextClick = () => {
if(activeStep === steps.length - 1)
handleSubmit()
else
handleNext()
}
...
<Button
variant="contained"
onClick={onNextClick}
sx={{ mt: 3, ml: 1 }}
>
{activeStep === steps.length - 1 ? 'Register' : 'Next'}
</Button>
...
I have Flatlist with an array of items and when I select an item, all other items take the state/color of the selected item grey. How do I ensure that only selected items changes state?
Also the items I select are stored in a firestore database and are deleted when unselected. Can you also help me to store this changed state into firestore db. Thank you and I appreciate your effort.
const [catalogueArray, setCatalogueArray] = useState([])
const [addCompare, setAddCompre] = useState(false)
const [textvalue, setTextValue] = useState(`Add to \n Cart`)
const [textColor, setTextColor] = useState('green')
const storeToDB = async (item) => {
if (!addCompare) {
await db.collection('users').doc(auth.currentUser.uid)
.collection('myProducts').doc(item.storeName + item.genName)
.set({
product_id: item.id,
product_genName: item.genName
})
} else {
await db.collection('users').doc(auth.currentUser.uid)
.collection('myProducts').doc(item.storeName + item.genName).delete()
}
}
const clickedBtn = () => {
setAddCompre(!addCompare ? true : false)
setTextValue(!addCompare ? `Item \n Added` : `Add to \n Cart`)
setTextColor(!addCompare ? 'grey' : 'green')
}
render(
....
<FlatList
keyExtractor={(item) => item.id}
data={catalogueArray}
renderItem={({ item }) => (
......
<TouchableOpacity style={styles.btn} onPress={() => { storeToDB(item); clickedBtn() }}>
<MaterialCommunityIcons name='plus-circle-outline' size={24} color={textColor} />
......
<Text style={...}>{textvalue}</Text>
</TouchableOpacity>
/>
const [isSelected, setIsSelected] = useState([])
const [addCompare, setAddCompare] = useState(false)
const catalogueList = () => {
const catalogue = data.filter(item => {
return item.storeName == navigation.state.params.selectStore
}).map(item => ({ ...item }))
setCatalogueArray(catalogue)
}
const storeToDB = async (item) => {
if (!addCompare) {
await db.collection('users').doc(auth.currentUser.uid).collection('myProducts').doc(item.storeName + item.genName).set({
product_id: item.id,
product_genName: item.genName
})
} else {
await db.collection('users').doc(auth.currentUser.uid).collection('myProducts').doc(item.storeName + item.genName).delete()
}
}
const clickedBtn = async (item) => {
setAddCompare(
addCompare ? false : true
)
if (isSelected.indexOf(item) > -1) {
let array = isSelected.filter(indexObj => {
if (indexObj == item) {
return false
}
return true
})
setIsSelected(array)
} else {
setIsSelected([
...isSelected, item
])
}
}
return (
....
<FlatList
extraData={isSelected}
keyExtractor={(item) => item.id}
data={catalogueArray}
renderItem={({ item }) => (
<View style={styles.contain}>
<TouchableOpacity style={styles.btn} onPress={() => { storeToDB(item); clickedBtn(item) }}>
<MaterialCommunityIcons name='plus-circle-outline' size={24} color={isSelected.indexOf(item) > -1 ? 'grey' : 'green'} />
<View style={{ position: 'absolute', bottom: 3 }}>
<Text style={{ fontSize: 10, textAlign: 'center', color: isSelected.indexOf(item) > -1 ? 'grey' : 'green' }}>{isSelected.indexOf(item) > -1 ? 'item \nAdded' : 'Add to\n Compare '}</Text>
</View>
</TouchableOpacity>
</View>
)}
/>
)
}```
I have 5 buttons in my app which I would like to change the background color based on button states, so now when I click one button it affects all the buttons (toggle-class), not only that I need to change the button color but also I need to hide and show data for each button, so I am using condition rendering, the default tab is social media. so, for example, u click button 1 it changes the background color and it shows div withe information, etc
Here is what I have so far
import React, { useState, useEffect, useRef } from 'react';
function Mata() {
const [isBlack, setIsBlack] = useState(0);
const [tab, setTab] = useState('socialmedia');
const handleBtn1 = (e) =>{
e.preventDefault();
setIsBlack(!isBlack);
setTab('data1);
}
const handleBtn2 = (e) =>{
e.preventDefault();
setIsBlack(!isBlack);
setTab('data2');
}
const handleBtn3 = (e) =>{
e.preventDefault();
setIsBlack(!isBlack);
setTab('data3');
}
const handleBtn4 = (e) =>{
e.preventDefault();
setIsBlack(!isBlack);
setTab('data4');
}
const handleBtn5 = (e) =>{
e.preventDefault();
setIsBlack(!isBlack);
setTab('data5');
}
return (
<div className="container">
<button style={{ backgroundColor: isBlack ? '#262626' : '#F3F3F3'}} className={`btn1 ${isBlack && activeTab}`} onClick={handleBtn1}>btn1</button>
<button style={{ backgroundColor: isBlack ? '#262626' : '#F3F3F3'}} className={`btn2 ${isBlack && activeTab}`} onClick={handleBtn2}>btn2</button>
<button style={{ backgroundColor: isBlack ? '#262626' : '#F3F3F3'}} className={`btn3 ${isBlack && activeTab}`} onClick={handleBtn3}>btn3</button>
<button style={{ backgroundColor: isBlack ? '#262626' : '#F3F3F3'}} className={`btn4 ${isBlack && activeTab}`} onClick={handleBtn4}>btn4</button>
<button style={{ backgroundColor: isBlack ? '#262626' : '#F3F3F3'}} className={`btn5 ${isBlack && activeTab}`} onClick={handleBtn5}>btn5</button>
{tab === 'socialmedia' && <>
....data
</div>
{tab === 'data1' && <>
....data
</div>
............
..........
</div>
)
}
export default Mata
What do I need to change to get this working?
You need individual state for each button. I suggest using a map to store a button id and a boolean value for whether it is "black" or not, i.e. the click handler simply toggles a boolean value. I don't know if it was a typo in copy/pasting code to SO, but the react state needs to be declared in in the functional component body.
const [isBlack, setIsBlack] = useState({});
You can also use a single click handler by converting it to a curried callback, taking and enclosing in scope the button id. This uses a functional state update to shallowly copy existing state and updates the value of the enclosed button id.
const handleBtn = btnId => e => {
e.preventDefault();
setIsBlack(state => ({
...state,
[btnId]: !state[btnId],
}));
};
Complete code
function Mata() {
const [activeTab, setActiveTab] = useState("activeTab");
const [isBlack, setIsBlack] = useState({});
const handleBtn = btnId => e => {
e.preventDefault();
setIsBlack(state => ({
...state,
[btnId]: !state[btnId]
}));
};
return (
<div className="container">
<button
style={{ backgroundColor: isBlack["btn1"] ? "#262626" : "#F3F3F3" }}
className={`btn1 ${isBlack["btn1"] && activeTab}`}
onClick={handleBtn("btn1")}
>
btn1
</button>
<button
style={{ backgroundColor: isBlack["btn2"] ? "#262626" : "#F3F3F3" }}
className={`btn2 ${isBlack["btn2"] && activeTab}`}
onClick={handleBtn("btn2")}
>
btn2
</button>
<button
style={{ backgroundColor: isBlack["btn3"] ? "#262626" : "#F3F3F3" }}
className={`btn3 ${isBlack["btn3"] && activeTab}`}
onClick={handleBtn("btn3")}
>
btn3
</button>
<button
style={{ backgroundColor: isBlack["btn4"] ? "#262626" : "#F3F3F3" }}
className={`btn4 ${isBlack["btn4"] && activeTab}`}
onClick={handleBtn("btn4")}
>
btn4
</button>
<button
style={{ backgroundColor: isBlack["btn5"] ? "#262626" : "#F3F3F3" }}
className={`btn5 ${isBlack["btn5"] && activeTab}`}
onClick={handleBtn("btn5")}
>
btn5
</button>
</div>
);
}
There is a lot of repeated code, so a more DRY version where active tab and buttons are passed as props.
function Mata({ activeTab = '', buttons }) {
const [isBlack, setIsBlack] = useState({});
const handleBtn = btnId => e => {
e.preventDefault();
setIsBlack(state => ({
...state,
[btnId]: !state[btnId]
}));
};
return (
<div className="container">
{buttons.map(btn => (
<button
style={{ backgroundColor: isBlack[btn] ? "#262626" : "#F3F3F3" }}
className={`btn1 ${isBlack[btn] && activeTab}`}
onClick={handleBtn(btn)}
>
{btn}
</button>
))}
</div>
);
}
Used as such
const buttons = ["btn1", "btn2", "btn3", "btn4", "btn5"];
...
<Mata buttons={buttons} />
Edit
Seems you are really creating a "tab manager". I suggest lofting state to the parent and converting Mata to a "dumb" component that simply renders the "tab" buttons. Takes 3 props: an active tab index, array of buttons, and a state update callback.
function Mata({ activeTab = -1, buttons, setActiveTab }) {
return (
<div className="container">
{buttons.map((btn, i) => {
const isActive = i === activeTab;
return (
<button
key={btn.id}
style={{ backgroundColor: isActive ? "#262626" : "#F3F3F3" }}
className={`${btn.id} ${isActive && activeTab}`}
onClick={() => setActiveTab(i)}
>
{btn.id}
</button>
);
})}
</div>
);
}
Example tabs data
const tabs = [
{ id: "btn1", data: "data1" },
{ id: "btn2", data: "data2" },
{ id: "btn3", data: "data3" },
{ id: "btn4", data: "data4" },
{ id: "btn5", data: "data5" }
];
Example usage
<Mata activeTab={activeTab} buttons={tabs} setActiveTab={setActiveTab} />
{activeTab === -1 ? (
<div>Social Media</div>
) : (
<div>{tabs[activeTab].data}</div>
)}
Adding "Icons"
Similar to Choosing the Type at Runtime
If SVG icons are not already react components, wrap them into a simple functional component
const Icon1 = () => <svg>...</svg>;
Add an icon field to the tabs data and set the value to the icon component
const tabs = [
{ id: "btn1", data: "data1", icon: Icon1 },
{ id: "btn2", data: "data2", icon: Icon2 },
{ id: "btn3", data: "data3", icon: Icon3 },
{ id: "btn4", data: "data4", icon: Icon4 },
{ id: "btn5", data: "data5", icon: Icon5 }
];
And destructure and rename to render
function Mata({ activeTab = -1, buttons, setActiveTab }) {
return (
<div className="container">
{buttons.map((btn, i) => {
const isActive = i === activeTab;
const { icon: Icon, id } = btn; // <-- rename icon -> Icon
return (
<button
key={id}
style={{ backgroundColor: isActive ? "#262626" : "#F3F3F3" }}
className={`${id} ${isActive && activeTab}`}
onClick={() => setActiveTab(i)}
>
<Icon /> {id} // <-- render icon component
</button>
);
})}
</div>
);
}
Why are you doing this
const [isBlack, setIsBlack] = useState(0);
instead of doing this ?
const [isBlack, setIsBlack] = useState(false);
Also to make use of useState you have to edit your code like the following, as hooks can only be called inside of the body of a function component.
import React, { useState, useEffect, useRef } from "react";
function Mata() {
const [isBlack, setIsBlack] = useState(false); // correction here
const handleBtn1 = e => {
e.preventDefault();
setIsBlack(!isBlack);
};
const handleBtn2 = e => {
e.preventDefault();
setIsBlack(!isBlack);
};
const handleBtn3 = e => {
e.preventDefault();
setIsBlack(!isBlack);
};
const handleBtn4 = e => {
e.preventDefault();
setIsBlack(!isBlack);
};
const handleBtn5 = e => {
e.preventDefault();
setIsBlack(!isBlack);
};
return (
<div className="container">
<button
style={{ backgroundColor: isBlack ? "#262626" : "#F3F3F3" }}
className={`btn1 ${isBlack && activeTab}`}
onClick={handleBtn1}
>
btn1
</button>
<button
style={{ backgroundColor: isBlack ? "#262626" : "#F3F3F3" }}
className={`btn2 ${isBlack && activeTab}`}
onClick={handleBtn2}
>
btn2
</button>
<button
style={{ backgroundColor: isBlack ? "#262626" : "#F3F3F3" }}
className={`btn3 ${isBlack && activeTab}`}
onClick={handleBtn3}
>
btn3
</button>
<button
style={{ backgroundColor: isBlack ? "#262626" : "#F3F3F3" }}
className={`btn4 ${isBlack && activeTab}`}
onClick={handleBtn4}
>
btn4
</button>
<button
style={{ backgroundColor: isBlack ? "#262626" : "#F3F3F3" }}
className={`btn5 ${isBlack && activeTab}`}
onClick={handleBtn5}
>
btn5
</button>
</div>
);
}
export default Mata;