multiselect-react-dropdown | selected values not updating onRemove and onSelect function

299 Views Asked by At

Hi i am using multiselect-react-dropdown for selecting multiple options in my admin panel.

Getting relational data from API, which is working fine. now when i load/render the edit page the selectedValues variable got empty in first 4 attempts & on 5th attempt this variable fill by value. that is the reason selectedValues not updating in edit page. let me share code.

Edit.js

import { useEffect, useState } from "react"
import { useDispatch, useSelector } from "react-redux"
import InputText from '../../../components/Input/InputText'
import { showNotification } from "../../common/headerSlice"
import { getCommonListings } from "../../../services/CommonService"
import TitleCard from "../../../components/Cards/TitleCard"
import MultiSelectBox from "../../../components/Input/MultiSelectBox"
import { showCategory, updateCategory } from "../../../services/CategoryService"
import { useNavigate, useParams } from "react-router-dom"
import InputFile from "../../../components/Input/InputFile"
import InputSlug from "../../../components/Input/InputSlug"
import { NO_IMAGE } from "../../../utils/globalConstantUtil"
import { list } from "postcss"

const INITIAL_CATEGORY_OBJ = {
    id : "",
    title : "",
    slug : "",
    websites : [],
    image : ""
}

const getIDs = (arr) => {
    let ids = [];
    arr.forEach(ar => {
        ids.push(ar.id);
    });
   return ids;
}

function Edit(){
    const params = useParams();
    const dispatch = useDispatch()
    const [loading, setLoading] = useState(false)
    const [selectedWebsites, setSelectedWebsites] = useState([]);
    const [errorMessage, setErrorMessage] = useState([])
    const [defaultImage, setDefaultImage] = useState("")
    const  websites  = useSelector(state => state.category.websites)
    const [categoryObj, setCategoryObj] = useState({})

    useEffect(() => {
        
        dispatch(getCommonListings())
        dispatch(showCategory({...params}))
        .unwrap()
        .then(res => {

            let get_category_detail = res.data;
            INITIAL_CATEGORY_OBJ.id = get_category_detail.id;
            INITIAL_CATEGORY_OBJ.title = get_category_detail.title;
            INITIAL_CATEGORY_OBJ.slug = get_category_detail.slug;
            INITIAL_CATEGORY_OBJ.websites = getIDs(get_category_detail.websites);
            setCategoryObj(INITIAL_CATEGORY_OBJ);
            setDefaultImage(get_category_detail.image);
            setSelectedWebsites(get_category_detail.websites)
        })

    }, [])
    
    const navigate = useNavigate();
    const doUpdate = () => {
        setLoading(true);
        console.log(categoryObj);
        dispatch(updateCategory({...categoryObj}))
        .unwrap()
        .then((res) => {
            if(res.success){
                dispatch(showNotification({message : res.message, status : 1}))
                navigate('/app/categories');
            }
            else{
                setErrorMessage(res.data)
                dispatch(showNotification({message : res.message, status : 0}))
            }
            setLoading(false);
            
        })
        .catch((e) => {
            dispatch(showNotification({message : e.message, status : 0}))
            setLoading(false);
        })
}

    const updateFormValue = ({updateType, value}) => {
        
        setCategoryObj({...categoryObj, [updateType]: value});
        
        if(updateType === 'title')
        setCategoryObj(prevState => ({...prevState, slug:value.replace(/\s+/g, '-').toLowerCase()})) 
        
    }

    const updateFileValue = (file) => {
        if(!file) 
        return
        setCategoryObj({...categoryObj, image : file})
    }

    const setupMultiSelectList = ({updateType, listObj}) =>{
        setCategoryObj({...categoryObj, websites : listObj.map(lst => lst.id)})
    }

    return(
        <>
            
        <TitleCard title="Edit Category" topMargin="mt-2">     
            <InputText type="text" labelTitle="Title"  error={errorMessage['title'] ? errorMessage['title']:''} updateType="title" defaultValue={categoryObj.title} updateFormValue={updateFormValue}/>
            <InputSlug error={errorMessage['slug'] ? errorMessage['slug']:''} slug={categoryObj.slug} />
            <div className="mask mask-squircle w-12 h-12">
                <img src={defaultImage ?  process.env.REACT_APP_URL+defaultImage : NO_IMAGE}   alt="Avatar" />
            </div>
            <InputFile type="file" error={errorMessage['image'] ? errorMessage['image']:''}  updateType="image" containerStyle="mt-4" labelTitle="Image" updateFileValue={updateFileValue}/>
           
            <MultiSelectBox 
                labelTitle='Websites'  
                updateType="websites"
                selectedValues={selectedWebsites}
                options={websites}  
                error={errorMessage['websites'] ? errorMessage['websites']:''}
                setupMultiSelectList={setupMultiSelectList}
            />
            <div className="mt-16"><button className="btn btn-primary float-right" onClick={() => doUpdate()}>Update</button></div>
        </TitleCard>
        </>
    )
}


export default Edit

MultiSelectBox.js

import Multiselect from "multiselect-react-dropdown"
import { useEffect, useState } from "react"

function MultiSelectBox({labelTitle, updateType, options, error, setupMultiSelectList, selectedValues}){
    
    console.log(selectedValues); //will show value on 5th attempt

    const [listObj, updateListObj] = useState(selectedValues);
    
    const onSelect = (selectedList, selectedItem) => { 
        updateListObj([...listObj, selectedItem])
    }
    const  onRemove = (selectedList, removedItem) => {
        updateListObj(listObj.filter(item => item.id !== removedItem.id));   
    }

    useEffect(() => {
        setupMultiSelectList({updateType,listObj})
    },[listObj])

    return(
        <div className="form-control w-full">
            <label className="label">
                <span className="label-text text-base-content">{labelTitle}</span>
            </label>
            <Multiselect
                options={options} 
                onSelect={onSelect}
                onRemove={onRemove}
                displayValue="name"
                selectedValues={selectedValues}
            />
            {error ? 
            <span className="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
            {error}
            </span>:
            ''
            }
        </div>
    )
}

export default MultiSelectBox

console output

enter image description here

1

There are 1 best solutions below

2
momin naveed On

you need to fix this.

 const [listObj, updateListObj] = useState(selectedValues);
    
    const onSelect = (selectedList, selectedItem) => { 
        updateListObj([...listObj, selectedItem])
    }
    const  onRemove = (selectedList, removedItem) => {
        updateListObj(listObj.filter(item => item.id !== removedItem.id));   
    }

    useEffect(() => {
        setupMultiSelectList({updateType,listObj})
    },[listObj])

try this instead


    const onSelect = (selectedList, selectedItem) => { 
        setupMultiSelectList({updateType,[...listObj,selectedItem]})
    }
    const  onRemove = (selectedList, removedItem) => {
              setupMultiSelectList({updateType,[...listObj,selectedItem].filter(item => item.id !== removedItem.id)    })

remove the local state and useEffect you don't need that here