📚 우리를 위한 기록

[ MUI ] Autocomplete option 사용 시 same key ERROR

Po_tta_tt0 2023. 3. 7. 20:37
반응형

 

 

0. 오류 상황 발생

  • Stackticon 프로젝트를 진행하면서 발생한 문제
  • Autocomplete에 들어가는 options(stack의 아이콘 정보 객체를 담은 배열)의 title이 중복돼서 발생했다
      <Autocomplete
        multiple
        id='tags-outlined'
        options={iconArr}
        getOptionLabel={(option) => option.title}
        onChange={onStackChange}
        filterSelectedOptions
        renderInput={(params) => (
          <TextField {...params} label='Choose User Stacks!' placeholder='Stacks' />
        )}
      />

 

 

1. 추론

궁금증

  1. Autocomplete에 들어가는 options의 key는 바꿀 수 없나?
  2. icon 객체 내의 다른 정보(path, hex, title ...) 중 왜 title을 key로 사용했을까?

생각

  1. 분명 Autocomplete의 key를 바꿀 방법이 있을 것이다
  2. 문제 상황에서 왜 title이 key였는지 알아보자

 

 

2. 시도

How can I add unique keys to React/MUI Autocomplete component?

[AutoComplete] getOptionLabel shouldn't be used as the option's key #29225
[Autocomplete] Not working properly with repeated options values #26492
MUI issue를 확인할 수 있었다.

 

이슈를 통해 알게 된 renderOption을 추가하고, Stackticon 프로젝트는 key 문제 없이 동작했다

 

수정한 코드

      <Autocomplete
        multiple
        id='tags-outlined'
        options={iconArr}
        renderOption={(props, option) => { // ✨ renderOption을 통해 key를 title이 아닌 고유값으로 지정했다
          return (
            <li {...props} key={option.path}>
              {option.title}
            </li>
          );
        }}
        getOptionLabel={(option) => option.title}
        onChange={onStackChange}
        filterSelectedOptions
        renderInput={(params) => (
          <TextField {...params} label='Choose User Stacks!' placeholder='Stacks' />
        )}
      />

 

 

궁금증 해결하기

문제는 해결됐지만 궁금증은 남았다.

 

  1. 분명 Autocomplete의 key를 바꿀 방법이 있을 것이다
  2. 문제 상황에서 왜 title이 key였는지 알아보자
    라는 추론을 하나하나 되짚어보자!

 

🚀
우선 공식문서를 확인해보자
Autocomplete API

options의 key를 바꿀 방법을 제공할까?

  • Props를 확인했지만.. 잘 모르겠다.

 

🚀
코드를 열어볼까?
MUI/useAutocomplete.js
여기서 도움을 얻을 수 있었다

 

💡 useAutocomplete.js

  let getOptionLabel = getOptionLabelProp;

  getOptionLabel = (option) => {
    const optionLabel = getOptionLabelProp(option);
    if (typeof optionLabel !== 'string') {
      if (process.env.NODE_ENV !== 'production') {
        const erroneousReturn =
          optionLabel === undefined ? 'undefined' : `${typeof optionLabel} (${optionLabel})`;
        console.error(
          `MUI: The \`getOptionLabel\` method of ${componentName} returned ${erroneousReturn} instead of a string for ${JSON.stringify(
            option,
          )}.`,
        );
      }
      return String(optionLabel);
    }
    return optionLabel;
  };

 

💡 MUI Props 중 getOptionLabel

Used to determine the string value for a given option. It's used to fill the input (and the list box options if renderOption is not provided).
If used in free solo mode, it must accept both the type of the options and a string.

Signature:
function(option: T) => string

 

getOptionLabel이라는 함수는
option을 받아서 optionsLabel : string을 return하는 함수다.

 

 

그러니까, 문제 상황에서 발생한 코드
getOptionLabel={(option) => option.title}
여기서 title을 return값으로 줬다는 것.

 

🤔 "음 그렇다면 이 return 값을 어디서 사용하는데?"

 

 

💡 useAutocomplete.js

    getOptionProps: ({ index, option }) => {
      const selected = (multiple ? value : [value]).some(
        (value2) => value2 != null && isOptionEqualToValue(option, value2),
      );
      const disabled = getOptionDisabled ? getOptionDisabled(option) : false;

      return {
        key: getOptionLabel(option), // 😮 바로 여기서!! 사용한다
        tabIndex: -1,
        role: 'option',
        id: `${id}-option-${index}`,
        onMouseOver: handleOptionMouseOver,
        onClick: handleOptionClick,
        onTouchStart: handleOptionTouchStart,
        'data-option-index': index,
        'aria-disabled': disabled,
        'aria-selected': selected,
      };
    },

getOptionProps에서 사용한다.

 

  1. selected는 multiple 속성의 true/false 여부에 따라 선택된 애들을 보여주는 것 같고(대충 넘어가자)
  2. disabled도 Option의 display 여부를 결정하는 것처럼 보인다
  3. 중요한 return을 보자. key가 getOptionLabel(option)이다!!!

 

따라서 title이 아니라 hex나 path로 값을 줬으면

  1. option으로 노출되는 값이 hex나 path가 되고
  2. key도 hex나 path가 된다
    => 따라서, 사용자에게 노출되는 값과 key가 같다고 생각하면 됨!

 

하지만 근본적인 해결은 아니다.

JizzyBob

`
The propsed fix of ensuring you key the renderOption doesn't entirely solve the problem.
There's two ways you get the non-unique console errors - non-unique outputs of renderOptions, 
and non-unique outputs of getOptionsLabel.

I appreciate there's a GIGO / bad UX argument to be made about resultsets that, 
while differing in ID, have identical data, but in systems 
that allow for user generated content undesirable data is going to happen.
I could include the record ID in getOptionsLabel to solve my uniqueness problem for the key, 
but now my users are having to look at an ID, which is just a different flavour of bad UX.
Single responsiblity principal appears to be rearing it's head again here.

I quite liked the idea of the getOptionKey solution #32910 but can see 
that's since been removed #33142.

I can't think of another solution at the moment,
possibly because my mind is anchored on getOptionKey, 
but I do hope someone can resolve this issue, 
cos the currently recommended workaround is not suitable for all circumstances.

P.S. I'm quickly becoming a MUI convert. 
Striking a balance between configurability and simplicity of implementation is tough, 
and so far you've all done a great job on that front.
`
  • key의 고유성 문제
  • ID(key)는 다르지만 동등한 데이터를 가지고 있는 시스템에서 발생할 수 있는 문제
    등이 남아있는것같다

오픈 소스 기여를 원하면 한 번 들여다봐도 좋을 것 같다😻

반응형