The problem is that the count needs to be subtracted from the total when the item on the list has been un-selected. So far it works when the item is selected and the count goes up. Maybe some kind of toggle is needed to subtract the total from the count. And if the count is zero it needs to show and empty div with no zero or numbers. thanks in advance if anyone can help out 😀🐍🏴☠️
Sandbox here : https://codesandbox.io/s/checkbox-counter-friday-28-fgp85?file=/src/App.js:0-948
export const DropdownMenu = (props) => {
const [selectedMenu, setSelectedMenu] = useState([]);
return (
<div className="flex justify-center relative -mr-10">
<div className="absolute top-10 w-240 opacity-100 bg-white -ml-48 shadow-md rounded-lg text-left p-3 z-30">
<span className="text-center inline-block w-3">
{selectedMenu.length}
</span>
{props.options &&
props.options.map((option, _idx) => (
<div key={option.id} className={props.titleAndSubtitleGroup}>
<div className="flex flex-row">
<input
onClick={() => {
!selectedMenu.includes(option) &&
setSelectedMenu((oldValue) => [...oldValue, option]);
}}
type="checkbox"
id={option.group}
className="bg-white border-2 rounded w-4 h-4 mt-1 flex flex-shrink-0 justify-center items-center mr-2 focus-within:border-blue-0"
/>
<label
htmlFor={option.groupId}
className={"no-underline hover:underline"}
>
{option.text}{" "}
</label>
</div>
</div>
))}
</div>
</div>
);
};
export default DropdownMenu;
import React from "react";
import DropdownMenu from "./DropdownMenu";
export const App = () => {
return (
<DropdownMenu
options={[
{
id: 1,
text: "Post",
icon: "",
url: "/#",
group: "01",
groupId: "01"
},
{
id: 2,
text: "Delete",
icon: "",
url: "/#",
group: "02",
groupId: "02"
},
{
id: 3,
text: "Forward",
icon: "",
url: "/#",
group: "03",
groupId: "03"
},
{
id: 4,
text: "Share",
icon: "",
url: "/#",
group: "04",
groupId: "04"
},
{
id: 5,
text: "Copy Link",
icon: "",
url: "/#",
group: "05",
groupId: "05"
}
]}
/>
);
};
export default App;```
You aren't removing elements from the selectedMenu array when the checkbox is unchecked.
I suggest using an object instead of an array to store a map of option id to checked status. Then get an array of values from the selectedMenu state and filter by what is selected to get a derived selected count.
const [selectedMenu, setSelectedMenu] = useState({});
...
<span className="text-center inline-block w-3">
{Object.values(selectedMenu).filter(Boolean).length}
</span>
or to conditionally only display non-zero values (this OFC can be optimized to be more DRY)
<span className="text-center inline-block w-3">
{!!Object.values(selectedMenu).filter(Boolean).length &&
Object.values(selectedMenu).filter(Boolean).length}
</span>
...
<input
onChange={e => {
const { id, checked } = e.target;
setSelectedMenu(selected => ({
...selected,
[id]: checked
}))
}}
type="checkbox"
id={option.group}
className="bg-white border-2 rounded w-4 h-4 mt-1 flex flex-shrink-0 justify-center items-center mr-2 focus-within:border-blue-0"
/>
OFC, if you wanted, or needed, to keep the array, then just conditionally filter the array, or append a value.
<input
onClick={() => {
selectedMenu.includes(option.id)
? setSelectedMenu((oldValue) =>
oldValue.filter((val) => val !== option.id)
)
: setSelectedMenu((oldValue) => [...oldValue, option.id]);
}}
type="checkbox"
id={option.group}
className="bg-white border-2 rounded w-4 h-4 mt-1 flex flex-shrink-0 justify-center items-center mr-2 focus-within:border-blue-0"
/>
Related
I have a form which has 2 fields.
Heading
Dynamic List which contains Name & Image
I'm able to validate changes whenever Heading or Name inside dynamic list is changed.
But when I change Image, the validation is failing. It returns true instead of showing error message. I tried adding a text input to test the image. Whenever I call onChange it is working fine.
Here is my code:
import { useForm } from "react-hook-form";
import { zodResolver } from "#hookform/resolvers/zod";
import * as z from "zod";
const schema = z.object({
heading: z
.string()
.min(3, { message: "Min 3 characters" })
.max(40, { message: "Max 40 characters" }),
testimonials: z.array(
z.object({
name: z.string().min(3, { message: "Min 3 characters" }),
image: z.string().url({ message: "Invalid URL" }),
})
),
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
});
const [dataObj, setDataObj] = useState({
"heading": "",
"testimonials": []
});
const fetchData = () => {
// fetch list from server
// bind data
setDataObj(dataFromServer);
};
fetchData();
const listChanged = (type, value, index) => {
let list = dataObj.data.testimonials;
if (type == "name") {
list[index].name = value;
} else if (type == "image") {
list[index].image = value;
}
setDataObj({
...dataObj,
testimonials: list,
});
};
const headingChanged = (value) => {
setDataObj({
...dataObj,
heading: value,
});
};
<form className="mt-10 space-y-6" onSubmit={handleSubmit(onSubmit)}>
<div
className={`relative appearance-none block w-full px-3 py-2 border rounded-md shadow-sm placeholder-gray-400 sm:text-sm ${
errors.heading
? "border-red-400 focus:outline-none focus:ring-red-500 focus:border-red-500"
: "border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
}`}
>
<label
htmlFor="name"
className="absolute -top-2 left-2 -mt-px inline-block bg-white px-1 text-xs font-medium text-gray-400"
>
Section Heading
</label>
<div className="mt-1">
<input
type="text"
autoComplete="name"
{...register("heading", {
required: true,
})}
className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm"
value={dataObj.data.title}
onChange={(e) => headingChanged( e.target.value)}
/>
</div>
{errors.heading && (
<small className="mt-2 text-xs text-red-500">
{errors.heading.message}
</small>
)}
</div>
<div className="mt-2 w-full py-2">
<div>
<div className="mt-2 flex justify-center mx-auto text-left">
<ul role="list" className="w-full">
{dataObj.data.testimonials.map((item, index) => (
<li key={index} className="py-3">
<div className="flex items-center space-x-4 justify-start w-full">
<div className="isolate -space-y-px rounded-md shadow-sm w-full">
<div
className={`appearance-none relative w-full px-3 py-2 border rounded-md rounded-b-none shadow-sm sm:text-sm ${
errors.testimonials?.[index]?.name
? "border-red-400 focus:outline-none focus:ring-red-500 focus:border-red-500"
: "border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
}`}
>
<label
htmlFor="name"
className="absolute -top-2 left-2 -mt-px inline-block bg-white px-1 text-xs font-medium text-gray-400"
>
Name
</label>
<input
{...register(`testimonials.${index}.name`)}
type="text"
onChange={(e) => {
listChanged(index, e.target.value, "name");
}}
value={item.name}
placeholder="New Label"
className="block w-full border-0 p-1.5 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm"
/>
<span className="invalid-feedback text-red-500 text-xs">
{errors.testimonials?.[index]?.name?.message}
</span>
</div>
<div className="relative rounded-md rounded-t-none border border-gray-300 px-3 py-2 focus-within:ring-0 focus-within:ring-indigo-600">
<label
htmlFor="name"
className="absolute -top-2 left-2 -mt-px inline-block bg-white px-1 text-xs font-medium text-gray-400"
>
Image
</label>
<input
{...register(`testimonials.${index}.image`)}
type="text"
value={item.image}
/>
<UploadImage
size={`small-round`}
index={index}
imageUrl={item.image}
imageChanged={listChanged}
/>
<span className="invalid-feedback text-red-500 text-xs">
{errors.testimonials?.[index]?.image?.message}
</span>
</div>
</div>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
</form>;
How do I solve this?
I have created following minimum example on how to use image with useFieldArray to create dynamic form. This has been created in codesandbox and tested for validation only. Let me know if you stuck anywhere or want more information.
SandBoxLink :
import React from "react";
import { useForm, useFieldArray } from "react-hook-form";
import { zodResolver } from "#hookform/resolvers/zod";
import * as z from "zod";
const MAX_FILE_SIZE = 200000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/png"];
const schema = z.object({
heading: z
.string()
.min(3, { message: "Min 3 characters" })
.max(40, { message: "Max 40 characters" }),
testimonials: z.array(
z.object({
name: z.string().min(3, { message: "Min 3 characters" }),
testimonial: z.string().min(3, { message: "Min 3 characters" }),
image: z
.any()
.refine(
(files) => files?.[0]?.size <= MAX_FILE_SIZE,
`Max image size is 2MB.`
)
.refine(
(files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
"Only .jpg, .png formats are supported."
)
})
)
});
const App = () => {
const appendValues = {
Name: "",
testimonial: "",
image: []
};
const defaultValues = {
heading: "",
testimonials: [appendValues]
};
const {
register,
handleSubmit,
control,
formState: { errors }
} = useForm({
defaultValues,
resolver: zodResolver(schema)
});
const { fields, append, remove } = useFieldArray({
control,
name: "testimonials"
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="heading">Heading : </label>
<input {...register("heading")} id="heading" />
<p>{errors?.heading?.message}</p>
{fields.map((field, index) => (
<div
key={field.id}
style={{
border: "1px solid black",
padding: "10px"
}}
>
<label htmlFor={`testimonials.${index}.name`}>Name : </label>
<input
id={`test.${index}.name`}
{...register(`testimonials.${index}.name`)}
/>
<p>{errors.testimonials?.[index]?.name?.message}</p>
<br />
<label htmlFor={`testimonials.${index}.testimonial`}>
Testimonial :
</label>
<input
id={`test.${index}.testimonial`}
{...register(`testimonials.${index}.testimonial`)}
/>
<p>{errors.testimonials?.[index]?.testimonial?.message}</p>
<br />
<label htmlFor={`testimonials.${index}.image`}>Image :</label>
<input
id={`test.${index}.image`}
type="file"
{...register(`testimonials.${index}.image`)}
/>
<p>{errors.testimonials?.[index]?.image?.message}</p>
<br />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => append(appendValues)}>
append
</button>
<input type="submit" />
</form>
);
};
export default App;
I have 2 select option fields using v-for loops from constants. One is for sides and another for caps. If a user selects 2-Sided in the sides selection, I want the active item under caps to show false.
<template>
...
<!-- Sides -->
<div class="mt-6">
<h3>Sides</h3>
<RadioGroup v-model="sides" class="mt-2">
<RadioGroupLabel class="sr-only"> Choose Sides </RadioGroupLabel>
<div class="grid grid-cols-3 gap-3 sm:grid-cols-4">
<RadioGroupOption as="template" v-for="option in sideOptions" :key="option.name" :value="option" :disabled="!option.active" v-slot="{ active, checked }">
<div :class="[option.active ? 'cursor-pointer focus:outline-none' : 'opacity-25 cursor-not-allowed', active ? 'ring-2 ring-offset-2 ring-indigo-500' : '', checked ? 'bg-indigo-600 border-transparent text-white hover:bg-indigo-700' : 'bg-white border-gray-200 text-gray-900 hover:bg-gray-50', 'border rounded-md py-3 px-3 flex items-center justify-center text-sm font-medium uppercase sm:flex-1']">
<RadioGroupLabel as="span">
{{ option.name }}
</RadioGroupLabel>
</div>
</RadioGroupOption>
</div>
</RadioGroup>
</div>
<!-- Caps -->
<div class="mt-6">
<h3>Caps</h3>
<RadioGroup v-model="caps" class="mt-2">
<RadioGroupLabel class="sr-only"> Choose Caps </RadioGroupLabel>
<div class="grid grid-cols-3 gap-3 sm:grid-cols-4">
<RadioGroupOption as="template" v-for="option in capOptions" :key="option.name" :value="option" :disabled="!option.active" v-slot="{ active, checked }">
<div :class="[option.active ? 'cursor-pointer focus:outline-none' : 'opacity-25 cursor-not-allowed', active ? 'ring-2 ring-offset-2 ring-indigo-500' : '', checked ? 'bg-indigo-600 border-transparent text-white hover:bg-indigo-700' : 'bg-white border-gray-200 text-gray-900 hover:bg-gray-50', 'border rounded-md py-3 px-3 flex items-center justify-center text-sm font-medium uppercase sm:flex-1']">
<RadioGroupLabel as="span">
{{ option.name }}
</RadioGroupLabel>
</div>
</RadioGroupOption>
</div>
</RadioGroup>
</div>
</template>
<script setup lang="ts">
...
const capOptions = [
{ name: 'No Cap', active: true },
{ name: '2-Sided', active: true },
{ name: '3-Sided', active: true },
{ name: '4-Sided', active: true },
]
const sideOptions = [
{ name: '2-Sided', active: true },
{ name: '3-Sided', active: true },
{ name: '4-Sided', active: true },
]
const caps = ref(capOptions[0])
const sides = ref(sideOptions[0])
</script>
You can bind update/select event to the sides drop-down. In the event handler, based on the value chosen, you can loop through the caps array and set the active to true or false. I hope the names of capOptions and sideOptions are in sync
const capOptions = [
{ name: 'No Cap', active: true },
{ name: '2-Sided', active: true },
{ name: '3-Sided', active: true },
{ name: '4-Sided', active: true },
]
const sideOptions = [
{ name: '2-Sided', active: true },
{ name: '3-Sided', active: true },
{ name: '4-Sided', active: true },
]
onSideSelect(name) {
const cap = capOptions.find(cap =>
cap.name === name);
cap.active = false;
}
Blur event is not working properly. It works if I click anywhere in the component except when clicking in the input field. If I click in the input field then outside the component, it won't trigger the blur event which closes the options list. How can I make the blur event on the outer div work after clicking on the input field and then clicking outside the component (* blur event should that be triggered if I click on the components list since it is still within the component, therefore I can't just place a blur event on the input field)
<template>
<div class="flex flex-col relative w-full">
<span v-if="label" class="font-jost-medium mb-2">{{ label }}</span>
<div>
<div #blur="showOptions = false" :tabindex="tabIndex">
<div
class="border border-[#EAEAEA] bg-white rounded-md flex flex-col w-full"
>
<div
v-if="selectedOptions.length"
class="flex flex-wrap px-4 py-2 border-b gap-2"
>
<div
v-for="option in selectedOptions"
class="border bg-secondary rounded-full py-1 px-2 flex items-center"
>
<span>{{ option.text }}</span>
<vue-feather
type="x"
class="h-3 w-3 ml-1.5 cursor-pointer"
#click="onDeleteOption(option)"
/>
</div>
</div>
<div
class="flex flex-row justify-end items-center px-4 cursor-pointer"
:class="selectedOptions.length ? 'py-2' : 'p-4'"
#click="showOptions = !showOptions"
>
<MagnifyingGlassIcon class="h-5 w-5 mr-2" />
<input
class="focus:outline-0 w-full"
type="text"
v-model="searchInput"
/>
<vue-feather type="chevron-down" class="h-5 w-5" />
</div>
</div>
<div v-if="showOptions && optionsMap.length" class="options-list">
<ul role="listbox" class="w-full overflow-auto">
<li
class="hover:bg-primary-light px-4 py-2 rounded-md cursor-pointer"
role="option"
v-for="option in optionsMap"
#mousedown="onOptionClick(option)"
>
{{ option.text }}
</li>
</ul>
</div>
<div
id="not-found"
class="absolute w-full italic text-center text-inactive-grey"
v-else-if="!optionsMap.length"
>
No records found
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watch } from "vue";
import { IconNameTypes } from "#/types/enums/IconNameTypes";
import { AppIcon } from "#/components/base/index";
import { MagnifyingGlassIcon } from "#heroicons/vue/24/outline";
export default defineComponent({
name: "AppAutocomplete",
components: {
AppIcon,
MagnifyingGlassIcon,
},
props: {
modelValue: {
type: String,
},
label: {
type: String,
default: "",
},
tabIndex: {
type: Number,
default: 0,
},
options: {
type: Array as PropType<{ text: string; value: string }[]>,
required: true,
},
},
setup(props, { emit }) {
const showOptions = ref(false);
const optionsMap = ref(props.options);
const selectedOptions = ref<{ text: string; value: string }[]>([]);
const searchInput = ref("");
watch(searchInput, () => {
optionsMap.value = props.options.filter((option1) => {
return (
!selectedOptions.value.some((option2) => {
return option1.text === option2.text;
}) &&
option1.text.toLowerCase().includes(searchInput.value.toLowerCase())
);
});
sortOptionsMapList();
});
const onOptionClick = (option: { text: string; value: string }) => {
searchInput.value = "";
selectedOptions.value.push(option);
optionsMap.value = optionsMap.value.filter((option1) => {
return !selectedOptions.value.some((option2) => {
return option1.text === option2.text;
});
});
sortOptionsMapList();
emit("update:modelValue", option.value);
};
const onDeleteOption = (option: { text: string; value: string }) => {
selectedOptions.value = selectedOptions.value.filter((option2) => {
return option2.text !== option.text;
});
optionsMap.value.push(option);
sortOptionsMapList();
};
const sortOptionsMapList = () => {
optionsMap.value.sort(function (a, b) {
return a.text.localeCompare(b.text);
});
};
sortOptionsMapList();
document.addEventListener("click", () => {
console.log(document.activeElement);
});
return {
showOptions,
optionsMap,
searchInput,
selectedOptions,
IconNameTypes,
onOptionClick,
onDeleteOption,
};
},
});
</script>
<style scoped lang="scss">
.options-list,
#not-found {
box-shadow: 0 0 50px 0 rgb(19 19 28 / 12%);
#apply border border-[#EAEAEA] rounded-md p-4 mt-1 absolute bg-white z-10 w-full;
}
ul {
#apply max-h-52 #{!important};
}
</style>
blur is not an event that 'bubbles up' to outer elements, so it never reaches the parent div. What you want is focusout
<div #focusout="showOptions = false" :tabindex="tabIndex">
This is the component that must be used.
Here is its source code:
import { Fragment, useState } from 'react'
import { Combobox, Transition } from '#headlessui/react'
import { CheckIcon, SelectorIcon } from '#heroicons/react/solid'
const people = [
{ id: 1, name: 'Wade Cooper' },
{ id: 2, name: 'Arlene Mccoy' },
{ id: 3, name: 'Devon Webb' },
{ id: 4, name: 'Tom Cook' },
{ id: 5, name: 'Tanya Fox' },
{ id: 6, name: 'Hellen Schmidt' },
]
export default function Example() {
const [selected, setSelected] = useState(people[0])
const [query, setQuery] = useState('')
const filteredPeople =
query === ''
? people
: people.filter((person) =>
person.name
.toLowerCase()
.replace(/\s+/g, '')
.includes(query.toLowerCase().replace(/\s+/g, ''))
)
return (
<div className="fixed top-16 w-72">
<Combobox value={selected} onChange={setSelected}>
<div className="relative mt-1">
<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm">
<Combobox.Input
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0"
displayValue={(person) => person.name}
onChange={(event) => setQuery(event.target.value)}
/>
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
<SelectorIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</Combobox.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
afterLeave={() => setQuery('')}
>
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{filteredPeople.length === 0 && query !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-gray-700">
Nothing found.
</div>
) : (
filteredPeople.map((person) => (
<Combobox.Option
key={person.id}
className={({ active }) =>
`relative cursor-default select-none py-2 pl-10 pr-4 ${
active ? 'bg-teal-600 text-white' : 'text-gray-900'
}`
}
value={person}
>
{({ selected, active }) => (
<>
<span
className={`block truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
{person.name}
</span>
{selected ? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? 'text-white' : 'text-teal-600'
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Transition>
</div>
</Combobox>
</div>
)
}
My question is: is it possible to show some information for an element when we hover over it?
For example, if we modify the input to this one:
const people = [
{ id: 1, name: 'Wade Cooper', info: 'test1' },
{ id: 2, name: 'Arlene Mccoy', info: 'test2' },
{ id: 3, name: 'Devon Webb', info: 'test3' },
{ id: 4, name: 'Tom Cook', info: 'test4' },
{ id: 5, name: 'Tanya Fox', info: 'test5' },
{ id: 6, name: 'Hellen Schmidt', info: 'test6' },
]
When we hover over first element, to show test1, for the second element test2 and so on.
I am not a react expert(/user). But for the Combobox component is 'active' the key word for hover. You have to conditionally render your displayed text/or the whole span like in pseudocode(however you conditionally render stuff in react):
active ? person.info : person.name
I have create a refinement component that sends data to its parent and it works correctly. after data goes to parent component, it will save to an array with useState. array value is ok when data from child component is incremental, but when the value from child component is decremented, the array value is not correct.
Here's the code:
Refinement.jsx
import { Fragment, useState } from "react";
const Refinement = (props) => {
let [fileTypeSelected, setFileTypeSelected] = useState([]);
let changingState = "";
const CheckBoxHandler = (event, content) => {
if (event) {
fileTypeSelected.push(content);
changingState = "Incremental"
}
else {
fileTypeSelected = fileTypeSelected.filter((c) => c !== content);
changingState = "Decremental"
}
}
return (
<Fragment>
<div className="w-full rounded-md overflow-hidden shadow-sm border">
<div className="flex justify-between items-center px-4 py-1 bg-biscay-700 text-white border-b">
<span className="font-semibold ">{props.RefineName}</span>
</div>
{
<div className={`px-4 py-2 w-full flex flex-col gap-2 transform transition-all duration-200 ${props.RefineValue.length <= 5 ? "" : "h-36 overflow-y-scroll scrollbar"} `}>
{
props.RefineValue.map(m => {
return (
<div className="text-sm flex justify-between items-center" key={m.id}>
<span>{m.displayName}</span>
<input className="accent-sky-500 w-4 h-4 rounded-md" onChange={(event) => {
CheckBoxHandler(event.target.checked, m.displayName);
props.onDataModified({
SearchCompnent: props.RefineName,
SearchItem: fileTypeSelected,
SearchState: changingState
});
}} type="checkbox" name="" id="" />
</div>
)
})
}
</div>
}
</div>
</Fragment>
)
}
export default Refinement;
App.jsx
import { Fragment, useState } from "react";
import Refinement from "../components/Refinement";
const App = () => {
const [fileFormatRefine, setfileFormatRefine] = useState([
{ id: 0, displayName: 'PDF' },
{ id: 1, displayName: 'DOCX' },
{ id: 2, displayName: 'PPTX' },
{ id: 3, displayName: 'XLSX' },
{ id: 4, displayName: 'TXT' },
{ id: 5, displayName: 'MP3' },
{ id: 6, displayName: 'MP4' },
{ id: 7, displayName: 'CS' },
]);
const [filterFormatModifiedDate, setFilterFormatModifiedDate] = useState([]);
let FileFormatQueryTemp = [];
const FileFormatModifiedEnvent = (enterdModifiedData) => {
const modifiedData = {
...enterdModifiedData
};
FileFormatQueryTemp = [];
for (let index = 0; index < modifiedData.SearchItem.length; index++) {
FileFormatQueryTemp.push([[modifiedData.SearchCompnent, modifiedData.SearchItem[index]]]);
}
setFilterFormatModifiedDate([]);
console.log(modifiedData.SearchItem.length);
setFilterFormatModifiedDate(FileFormatQueryTemp);
}
return (
<Fragment>
<div className="w-full lg:w-5/6 mx-auto flex gap-4 mt-16 px-4">
<div className="w-1/4 col-start-1 hidden lg:flex">
<form className="flex flex-col gap-4 w-full" id="Form" onSubmit={(event) => { event.preventDefault() }}>
<Refinement onDataModified={FileFormatModifiedEnvent} MinToShow={3} RefineValue={fileFormatRefine} RefineName="File Format"></Refinement>
<button className="h-8 text-white font-semibold text-sm rounded bg-red-600" onClick={() => { console.log(filterFormatModifiedDate.length) }}>Show Array Length</button>
</form>
</div>
<div className="lg:w-3/4 w-full flex flex-col gap-2">
<div className="w-full">
<h3 className="text-xs flex items-center font-semibold uppercase mb-2">Filters
<span className="h-4 w-4 ml-2 text-xs flex items-center justify-center rounded bg-gray-200 text-black">{filterFormatModifiedDate.length}</span>
</h3>
<div className="">
{
filterFormatModifiedDate.map(f => {
return (
<p key={f[0][1]} >{f[0]}</p>
)
})
}
</div>
</div>
</div>
</div>
</Fragment>
)
}
export default App;
if you wanna change fileTypeSelected state, you should do next:
setFileTypeSelected(prevArray => [...prevArray, content])
and in else scope:
setFileTypeSelected(prevValue => prevValue.filter((c) => c !== content));
You must always change state using his function, which comes inside array as second argument
let's consider this example
const [count,setCount] = useState(0)
setCount is the only function that can change count value