Использование OnChange, как изменить значения внутри <ArrayInput> <SimpleFormIterator><ReferenceInput> в React-Admin

У меня есть следующий компонент, который я пытаюсь использовать в «Представлении редактирования напитков». В «напитки» я вложил «ингредиенты», но ингредиенты также являются связанным ресурсом.

Я хочу выбрать ингредиент из связанного ресурса и установить вложенные поля (в напитке) «record.ingredients[idx].name», «record.ingredients.[idx]._id», «record.ingredients.[idx]». .единица".

Но я не могу изменить вложенность, даже если я использую индекс [0] для проверки, это не работает, и я тоже не знаю, как получить индекс.

Как сейчас, например, если я изменю ингредиент, название напитка будет изменено, потому что компонент находится внутри представления редактирования напитка.

Я попробовал FormDataConsumer, RecordContext, но безуспешно.

import {
    AutocompleteInput, ArrayInput, SimpleFormIterator, ReferenceInput, TextInput, NumberInput, BooleanInput,
    AutocompleteInputProps, FormDataConsumer,
} from 'react-admin';
import { useFormContext } from 'react-hook-form';

export const ArrayInputForIngredients = () => {
    const { setValue } = useFormContext();
    const handleIngredientChange: AutocompleteInputProps['onChange'] = (
        value,
        record
    ) => {
        setValue('name', record?.name); // NOT WORKING, CHANGES THE NAME OF THE DRINK
        setValue('unit', record?.unit);
        setValue('_id', record?._id);
    };

    return (
        <ArrayInput source = "ingredients">
            <SimpleFormIterator inline >
                <ReferenceInput source = "_id" reference = "ingredients" >
                    <AutocompleteInput label = "Ingrediente" 
                    source = "_id" 
                    optionText = {"name"} 
                    onChange = {handleIngredientChange} />
                </ReferenceInput>
                <TextInput source = "name" />
                <TextInput source = "unit" />
                <BooleanInput source = "decorative" />
                <BooleanInput source = "optional" />
                <NumberInput source = "quantity" />
            </SimpleFormIterator>
        </ArrayInput>
    )
}

Компонент находится внутри чего-то (упрощенного) вроде этого:

export const DrinksEdit = () => {
    return (
        <Edit>
        <SimpleForm>
        <TextInput source= "name" />
        <ArrayInputForIngredients />
        < /SimpleForm>
        < /Edit>
    )
};

Название и другие данные каждого ингредиента, которые я выбираю в AutoCompleteInput для изменения, а не название напитка.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если я правильно понимаю, вам нужно предварительно заполнить значения name и unit на основе выбранного ингредиента, на который указывает ссылка, верно?

Ответ для реакции-администратора v4

Если это так, прямой вызов setValue('name', record?.name) не может работать, потому что вам нужно использовать getSource FormDataConsumer для вычисления исходного пути для входных данных, вложенных внутри ArrayInput.

Один из способов добиться этого — использовать HOF следующим образом:

const TagsInput = () => {
  const { setValue } = useFormContext();
  const onChange = (source) => (_id, record) => {
    setValue(source, record?.name?.en);
  };
  return (
    <ArrayInput source = "tags">
      <SimpleFormIterator inline>
        <FormDataConsumer>
          {({ getSource }) => (
            <ReferenceInput source = {getSource("id")} reference = "tags">
              <AutocompleteInput
                label = "Tags"
                source = "id"
                optionText = {"name.en"}
                onChange = {onChange(getSource("name"))}
              />
            </ReferenceInput>
          )}
        </FormDataConsumer>
        <TextInput source = "name" />
      </SimpleFormIterator>
    </ArrayInput>
  );
};

А потом

export const PostEdit = () => {
    return (
        <Edit>
          <SimpleForm>
            <TextInput source= "name" />
            <TagsInput />
          </SimpleForm>
        </Edit>
    )
};

Рабочий пример в следующей песочнице (с вводом тегов, в PostEdit, вкладка «Разное»): https://codesandbox.io/p/devbox/young-platform-z7x5lf


Обновлено: Обновлена ​​песочница для реакции-администратора v5.

https://stackblitz.com/edit/github-vakown?file=src%2Fposts%2FPostEdit.tsx

Соответствующий код:

const TagAutocompleteInput = () => {
  const sourceContext = useSourceContext();
  const nameInputSource = sourceContext.getSource('name');
  const { setValue } = useFormContext();
  const onChange = (_id, record) => {
    setValue(nameInputSource, record?.name?.en);
  };
  return (
    <ReferenceInput source = "id" reference = "tags">
      <AutocompleteInput
        label = "Tags"
        source = "id"
        optionText = {'name.en'}
        onChange = {onChange}
      />
    </ReferenceInput>
  );
};

const TagsInput = () => (
  <ArrayInput source = "tags">
    <SimpleFormIterator inline>
      <TagAutocompleteInput />
      <TextInput source = "name" />
    </SimpleFormIterator>
  </ArrayInput>
);

Это использует useSourceContext из реакции-администратора.

Спасибо! Я пробовал много вариантов, но безуспешно, но с таким источником аргументов, как вы, все проблемы были решены. Я приведу свой ответ со своим кодом ниже только потому, что я также включил модификацию для изменения входной «единицы измерения».

Diego Gaona 06.05.2024 21:37

Благодаря @ slax57 мне было трудно заставить его работать (я попробовал FormDataConsumer, но не смог заставить его работать правильно). Я помещу свой ответ здесь, потому что я также включил решение по изменению входной «единицы». Теперь onChange принимает 2 источника.

export const ArrayInputForIngredients = () => {
    const { setValue } = useFormContext();
    const onChange = (sourceOne, sourceTwo) => (_id, record) => {
      setValue(sourceOne, record?.name);
      setValue(sourceTwo, record?.unit);
    };
    return (
      <ArrayInput source = "ingredients">
        <SimpleFormIterator inline>
          <FormDataConsumer>
            {({ getSource }) => (
              <ReferenceInput source = {getSource("_id")} reference = "ingredients">
                <AutocompleteInput
                  label = "Ingrediente"
                  source = "_id"
                  optionText = {"name"}
                  onChange = {onChange(getSource("name"), getSource("unit"))}
                />
              </ReferenceInput>
            )}
          </FormDataConsumer>
          <TextInput source = "name" />
          <TextInput source = "unit" />
        </SimpleFormIterator>
      </ArrayInput>
    );
  };

Решение React-Admin V5

Теперь вставляю поля, причем без GetSource (убрано на V5)

export const ArrayInputForIngredients = () => {
    const { data, isLoading } = useGetList('ingredients');

    const IngredientAutocompleteInput = () => {
        const sourceContext = useSourceContext();

        const nameInputSource = sourceContext.getSource('name');
        const unitInputSource = sourceContext.getSource('unit');
        const abvInputSource = sourceContext.getSource('abv');


        const { setValue } = useFormContext();
        const onChangeIngredient = (_id, record) => {
            setValue(nameInputSource, record?.name);
            setValue(unitInputSource, record?.unit);
            setValue(abvInputSource, record?.abv);
        };
        return (
            <>
                <ReferenceInput source = {"_id"} reference = "ingredients"
                    sort = {{ field: 'name', order: 'ASC' }} >
                    <Grid item xs = {12} md = {6} xl = {3} >
                        <AutocompleteInput
                            label = "Ingrediente"
                            source = "_id"
                            validate = {required("Nome necessário")}
                            optionText = "name"
                            isLoading = {isLoading}
                            onChange = {onChangeIngredient}
                            filterToQuery = {searchText => ({ name: searchText })}
                            fullWidth
                        />
                    </Grid>
                </ReferenceInput>
                <TextInput source = "name" sx = {{
                    display: "none",
                }} />
                <NumberInput source = "abv" sx = {{
                    display: "none",
                }} />
            </>
        )

    }

А затем используйте его как компонент:

<SimpleFormIterator inline >
                        <IngredientAutocompleteInput />
</SimpleFormIterator>

Другие вопросы по теме