Skip to content

Commit

Permalink
fix: uid prop to pass own unique id to prevent ssr issues
Browse files Browse the repository at this point in the history
fixes #85
  • Loading branch information
vitalybaev committed Apr 3, 2022
1 parent fa0b079 commit 2f78337
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 2 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@
"react/button-has-type": "off",
"react/jsx-no-target-blank": "warn",
"array-callback-return": "warn",
"react/destructuring-assignment": "off",
"no-underscore-dangle": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/interface-name-prefix": "off",
Expand Down
33 changes: 33 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const [value, setValue] = useState();
| highlightClassName | Нет | string | CSS класс элемента, подсвечивающего совпадения при наборе, если не передан, используется класс для стилей из коробки. |
| customInput | Нет | Element or string | Кастомный компонент поля ввода, например от Styled Components |
| selectOnBlur | Нет | boolean | Если `true`, то при потере фокуса будет выбрана первая подсказка из списка |
| uid | Нет | string | Уникальный ID который используется внутри компонента для связывания элементов при помощи aria атрибутов |

## Методы

Expand Down Expand Up @@ -240,6 +241,38 @@ const [value, setValue] = useState<DaDataSuggestion<DaDataAddress> | undefined>(
<AddressSuggestions token="API_KEY" value={value} onChange={setValue} />;
```

## Ошибка в консоли `Prop aria-owns did not match...`

Данная ошибка возникает при использовании серверного рендеринга. Под капотом, `react-dadata`, следуя принципам доступности, создает компонент с aria ролью "combobox", которому необходимо через обычные HTML идентификаторы связывать различные элементы. При использовании SSR в виду текущей архитектуры компонента данные ID генерируются дважды независимо: на сервере и на клиенте, из-за чего в момент регидратации React выявляет несовпадение этих идентификаторов. Эта проблема решается довольно просто в функциональных компонентах, однако на данный момент у меня нет быстрого решения этой проблемы.

Чтобы иметь возможность избавиться от данной ошибки при использовании SSR можно передавать пропс `uid`, в которой вы можете передать _уникальный в рамках страницы_ строковый идентификатор.

Если заранее известно, сколько компонентов и в каких местах страницы будут располагаться, можно передавать в качестве идентификаторов понятные строки:

```tsx
<AddressSuggestions
token="API_KEY"
value={value}
onChange={setValue}
uid="dadata-address-order-page"
/>;
```

Если вы уже обновились на React 18, то можно воспользоваться стандартным хуком `useId`:

```tsx
const id = useId();

return (
<AddressSuggestions
token="API_KEY"
value={value}
onChange={setValue}
uid={id}
/>
);
```

## Лицензия

```
Expand Down
13 changes: 11 additions & 2 deletions src/BaseSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export abstract class BaseSuggestions<SuggestionType, OwnProps> extends React.Pu

protected dontPerformBlurHandler = false;

protected uid: string;
protected _uid?: string;

protected didMount: boolean;

Expand All @@ -59,7 +59,6 @@ export abstract class BaseSuggestions<SuggestionType, OwnProps> extends React.Pu
constructor(props: BaseProps<SuggestionType> & OwnProps) {
super(props);

this.uid = nanoid();
this.didMount = false;

const { defaultQuery, value, delay } = this.props;
Expand Down Expand Up @@ -100,6 +99,16 @@ export abstract class BaseSuggestions<SuggestionType, OwnProps> extends React.Pu
this.didMount = false;
}

get uid(): string {
if (this.props.uid) {
return this.props.uid;
}
if (!this._uid) {
this._uid = nanoid();
}
return this._uid!;
}

protected getSuggestionsUrl = (): string => {
const { url } = this.props;

Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/AddressSuggestions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,16 @@ describe('AddressSuggestions', () => {
expect(handleChangeMock.mock.calls.length).toBe(1);
expect(handleChangeMock.mock.calls[0][0].value).toBe('г Москва');
});

it('uses uid prop', async () => {
const { rerender } = render(<AddressSuggestions token="TEST_TOKEN" />);

const combobox = await screen.findByRole('combobox');
expect(combobox.getAttribute('aria-owns')).toBeTruthy();
expect(combobox.getAttribute('aria-owns')).toEqual(combobox.getAttribute('aria-controls'));

rerender(<AddressSuggestions token="TEST_TOKEN" uid="dadata-address-order-page" />);
expect(combobox.getAttribute('aria-owns')).toBe('dadata-address-order-page');
expect(combobox.getAttribute('aria-controls')).toBe('dadata-address-order-page');
});
});
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface CommonProps<SuggestionType> {
minChars?: number;
customInput?: ElementType;
selectOnBlur?: boolean;
uid?: string;
}

export interface DaDataAddressMetro {
Expand Down

0 comments on commit 2f78337

Please sign in to comment.