Search code examples
reactjsreact-dropdown-tree-select

Why can't I recover selected items from a React Dropdown Tree Select


I'm having all kinds of trouble recovering values from a ReactDropdownTreeSelect. The component seems to keep it's state just fine, up until I want save the results of a change. However I try to do this, the component loses it's state and nothing is selected. What am i missing?

This is a simplest example, nearly identical to the one in the help, that shows the problem. One line breaks it...

import React, { useState, useEffect } from "react"
import DropdownTreeSelect from "react-dropdown-tree-select"

function DebugSelector() {
    const [mySelected, setMySelected] = useState([]);
    const [lastChanged, setLastChanged] = useState({});

    const tarifs = {
        label: "all tarifs", value: "all", children: [
            { label: "second level tarif", value: "secondLevel" },
            { label: "another 2nd level", value: "secondLevel2" }
        ]
    }
    const onChange = function (currNode, selectedNodes) {
        debugger
        // Any of these lines will break it. The component loses its state.
        //setMySelected(selectedNodes);
        //setMySelected([...selectedNodes]);
        //setLastChanged(currNode);
        setLastChanged(JSON.parse(JSON.stringify(currNode)))
    }
    return (
        <DropdownTreeSelect data={tarifs} onChange={onChange}></DropdownTreeSelect>
    )
}
export default DebugSelector;

Solution

  • To preserve its state, react-dropdown-tree-select needs to be wrapped in a class component that prevents re-renders if props have not changed. The class component can then proxy the onChange handler. This is my working wrapper...

    import React, { Component } from "react"
    import DropdownTreeSelect from "react-dropdown-tree-select"
    import isEqual from "lodash/isEqual"
    import { TarifForTreeSelect } from "interfaces/TarifForTreeSelect"
    
    interface IProps {
      data: TarifForTreeSelect
      onChange: (currentNode: any, selectedNodes: any) => void
      hasError?: any
      contentTitle?: string
      triggerTitle?: string
      isLoading?: boolean
    }
    
    interface IState {
      data: TarifForTreeSelect
    }
    
    class TarifsSelector extends Component<IProps, IState> {
      constructor(props: IProps) {
        super(props)
        this.state = { data: props.data }
      }
    
      componentWillReceiveProps = (nextProps: IProps) => {
        if (!isEqual(nextProps.data, this.state.data)) {
          this.setState({ data: nextProps.data })
        }
      }
    
      shouldComponentUpdate = (nextProps: IProps) => {
        return !isEqual(nextProps.data, this.state.data)
      }
    
      render() {
        const onChangeTemp = (currentNode: any, selectedNodes: any) => {
          this.props.onChange(currentNode, selectedNodes)
        }
    
        return (
          <>
            <DropdownTreeSelect data={this.state.data && this.state.data.value !== "" ? this.state.data : []} onChange={onChangeTemp} showDropdown="always" />
          </>
        )
      }
    }
    
    export default React.memo(TarifsSelector)