Панель закусок Material ui отображается только один раз и не открывается повторно

#reactjs #material-ui

Вопрос:

Я использую компонент Snackbar из фреймворка Material-UI, но я не могу заставить его работать правильно, потому что панель закусок отображается только в первый раз, со второго сообщения панель закусок остается закрытой.

Вот код, который я использовал для упаковки закусочной:

 import Snackbar from "@material-ui/core/Snackbar";
import { useState } from "react";

export default function Notification({ message }) {

    const [value, setValue] = useState(message);

    return (
        <Snackbar
            key={Math.random()}
            anchorOrigin={{
                vertical: 'bottom',
                horizontal: 'center'
            }}
            open={value ? true : false}
            autoHideDuration={5000}
            message={value}
            onClose={() => { console.log('snackbar closed'); setValue('') }}
        />
    )
};
 

Я использую этот компонент для отображения результата, когда пользователь отправляет форму. Например, при успешной отправке отображается сообщение «операция завершена», но если сервер не проверяет отправку, отображается сообщение об ошибке. Вот код:

 function InnerForm({ mode, detail, entity }) {

  const isCreateMode = mode === 'create';
  const schema = createSchema(entity);

  const {
    handleSubmit,
    formState: { errors },
    control,
    setValue
  } = useForm({
    defaultValues: detail,
    resolver: yupResolver(schema)
  });

  const [submitResponse, setSubmitResponse] = useState();

  const onSubmit = async data => {

    const operation = isCreateMode ?
      entity.Client.create(data) :
      entity.Client.update(data);

    const response = await operation;

    setSubmitResponse(response);
    console.log(response);

    if (response.success) {

      if (isCreateMode) {
        const autoField = entity.Fields.find(({ auto }) => auto);

        if (autoField) {
          setValue(autoField.id, response.content[autoField.id]);
        }
      }
    }
  };

  return (
    <>
      {
        submitResponse ?
          submitResponse.success ?
            <Notification message='operation completed' /> :
            submitResponse.problem ?
              <Notification message={submitResponse.problem} /> :
              <Notification message='operation failed' />
          : null
      }
      <form onSubmit={handleSubmit(onSubmit)}>
        {
          entity.Fields.map(field => {
            const props = {
              control,
              id: field.id,
              label: field.label,
              error: errors[field.id]?.message,
              multiline: field.type === 'text',
              loadOptions: field.loadOptions,
              getOptionLabel: field.getOptionLabel,
              readonly: submitResponse?.success || field.auto || (field.key amp;amp; !isCreateMode)
            };

            const Component =
              field.type === 'string' ? TextInput :
                field.type === 'datetime' ? DatetimeInput :
                  field.type === 'text' ? TextInput :
                    field.type === 'entity' ? SelectInput :
                      field.type === 'bool' ? CheckboxInput :
                        TextInput;

            return <Component {...props} />
          })
        }

        <SubmitButton isCreateMode={mode === 'create'} readonly={submitResponse?.success} />
      </form>
    </>
 

Тест, который я провожу, заключается в следующем:

  • Сначала я компилирую форму с неверными данными и выполняю команду отправить => >Ок, на панели закусок появляется сообщение об ошибке.
  • Во-вторых, я исправляю данные и выполняю новую отправку => >Отправка прошла успешно, но сообщение об успешном выполнении не отображается

Ответ №1:

Это не похоже на то , что вы обновляете state , как только он получает новое props , только инициализируете state его при первом создании.

Вы можете попробовать обновить state его, когда он будет вызван во второй раз.

Вы также можете сохранить open состояние внутри InnerForm компонента и добавить функцию обратного вызова в качестве параметра Notification , который будет вызван, onClose чтобы обновить состояние Snackbar внутри InnerForm

Пример второго предложения:

В InnerForm

 const [isOpen, setIsOpen] = useState(false);
const [message, setMessage] = useState(message);

const callback = () => {
    setValue(false)
}

// In render() you need to add this line.
<Notification message={message} isOpen={isOpen} onClose={callback}/>
 

В Notification

 export default function Notification({ message, isOpen, onClose }) {

return (
    <Snackbar
        key={Math.random()}
        anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'center'
        }}
        open={isOpen}
        autoHideDuration={5000}
        message={message}
        onClose={onClose}
    />
)
};
 

Наконец, вам просто нужно обновить message и isOpen в InnerForm том месте, где вы в данный момент вызываете Notification функцию

Комментарии:

1. Спасибо за помощь, вы решили мою проблему. Тогда компонент не сбрасывает свое состояние при изменении реквизита?

2. Компоненты не обновляют состояние автоматически при получении новых реквизитов. Причина, по которой вы получили сообщение внутри состояния, заключалась в том, что состояние инициализируется значением сообщения во время первого рендеринга. Я считаю, что это способ обновить реквизит с помощью крючков: useEffect(() => { setValue(message) }, [message]); . Если вы предпочитаете, вы можете использовать свой код с этим изменением. Хотя лично я считаю, что для родительского компонента имеет больше смысла InnerForm решать, когда Notification он должен быть открыт, как в моем примере в ответе выше.