react-functional-select icon indicating copy to clipboard operation
react-functional-select copied to clipboard

Capturing input value and using it as the selected option

Open Mitchell8210 opened this issue 4 years ago • 5 comments

Hello, I am trying to implement the react-functional-select component into an application that I am working on, I am wondering if there is a way to capture the input from the user and be able to use that value as the selected option. Is this part of the functionality that is built into this component?

I have a list of options that exist already, my goal is to be able to capture the input from the user in the case that what they typed in does not match any of the options that are available in the predefined set.

Mitchell8210 avatar Jan 14 '20 22:01 Mitchell8210

Hey, @Mitchell8210 - Can you provide a simplified code snippet outlining the workflow you're describing? Are you wanting to not display any options and require the user to input an exact match to one of the potential values and auto-select the option instead of requiring an explicit selection?

based-ghost avatar Jan 16 '20 00:01 based-ghost

Hey,

Firstly thank you for this library - It has been amazing so far :).

I would also like to do this - i.e. capture the input value and use it as the selected option:

For Example, if I start with 3 options: ['cat', 'dog', 'mouse']

I would like to allow users to type in 'horse' and upon pressing enter, the original options array is expanded to include horse, which would then be selected. (On a page refresh, I would want it to defaults back to the original option selection).

This is working nicely.

However, I am unable to capture the input value with onKeyDown. I have tried to use React References but I haven't had much luck.

This is what I have currently:

import React, { useState, useCallback, useRef } from 'react';
import { Select } from 'react-functional-select';

const initialOptions = [
  { id: 0, val: 'Cat' },
  { id: 1, val: 'Mouse' },
  { id: 2, val: 'Dog' },
];

function SelectColumnFilter() {
  const [currentValue, setCurrentValue] = useState(null);
  const [currentOptions, setCurrentOptions] = useState(initialOptions);
  const selectRef = useRef(null);

  const getOptionValue = useCallback((option) => option.id, []);
  const getOptionLabel = useCallback((option) => option.val, []);

  const onOptionChange = useCallback((option) => {
    if (option != null) {
      setCurrentValue(option.val);
    } else {
      setCurrentValue(null);
    }
  }, []);

  const onKeyDown = useCallback((e) => {
    var keycode = e.keyCode ? e.keyCode : e.which;
    if (keycode == '13') {
      console.log(e);
      console.log(selectRef.current);

      // I would like to get the current inputted value here.
      const newOption = { id: currentOptions.length, val: 'Horse' };
      const nextOptions = [...currentOptions, newOption];
      setCurrentOptions(nextOptions)
    }
  }, []);

  return (
      <Select
        ref={selectRef}
        options={currentOptions}
        onOptionChange={onOptionChange}
        getOptionValue={getOptionValue}
        getOptionLabel={getOptionLabel}
        onKeyDown={onKeyDown}
        initialValue={currentValue}
      />
  );
}

export default SelectColumnFilter;

IsmailM avatar Aug 05 '20 23:08 IsmailM

@IsmailM - I just recently published changes for v2.7.1 that should help address the scenario you outline. So, once updating to the latest version, your example above can be changed to the revised snippet below. A few key things to note, the onKeyDown callback property now gets the value of the input control and the currently focused option forwarded to it. The focusedOption param can come into play in case you want to make sure the currently entered search input does not match any other options (e.g. that it is unique) and you can check for that by using focusedOption.index === -1 which will be true if none of the current menuOptions is focused in the menu list. Also, if all the conditions are met, it may be a good idea to use e.preventDefault(), so that the normal handler does not get executed (although, this may not matter much in this scenario).

import React, { useState, useCallback, useRef } from "react";
import { Select } from "react-functional-select";

const initialOptions = [
  { id: 0, val: "Cat" },
  { id: 1, val: "Mouse" },
  { id: 2, val: "Dog" },
];

function SelectColumnFilter() {
  const [currentValue, setCurrentValue] = useState(null);
  const [currentOptions, setCurrentOptions] = useState(initialOptions);
  const selectRef = useRef(null);

  const getOptionValue = useCallback((option) => option.id, []);
  const getOptionLabel = useCallback((option) => option.val, []);

  const onOptionChange = useCallback((option) => {
    if (option != null) {
      setCurrentValue(option.val);
    } else {
      setCurrentValue(null);
    }
  }, []);

  const onKeyDown = useCallback((e, input, focusedOption) => {
    var keycode = e.keyCode ? e.keyCode : e.which;
    if (keycode === "13" && input && focusedOption.index === -1) {
      e.preventDefault();
      
      setCurrentOptions((prevOptions) => [
        ...prevOptions,
        { id: prevOptions.length, val: input },
      ]);
    }
  }, []);

  return (
    <Select
      ref={selectRef}
      options={currentOptions}
      onOptionChange={onOptionChange}
      getOptionValue={getOptionValue}
      getOptionLabel={getOptionLabel}
      onKeyDown={onKeyDown}
      initialValue={currentValue}
    />
  );
}

export default SelectColumnFilter;

based-ghost avatar Aug 10 '20 01:08 based-ghost

This looks amazing! Thank you very much for taking the time to implement this 👍

IsmailM avatar Aug 10 '20 01:08 IsmailM

@IsmailM - no problem, let me know if you find any issues with the recent changes. Thanks for the feedback and insights!

based-ghost avatar Aug 11 '20 13:08 based-ghost