Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material ui Snackbar show only once and doesn't reopen

I'm using the Snackbar component from the Material-UI framework but I can't make it work correctly because the Snackbar shows only the first time, from the second message the Snackbar remain closed.

Here is the code I used to wrap the Snackbar:

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('') }}
        />
    )
};

I use this component to show the result when the user submit a form. For example when the submit is succesful the message 'operation completed' is shown, but if the server doesn't validate the submission a message error is shown. Here is the code:

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 && !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>
    </>

The test I do is the following:

  • First, I compile the form with invalid data and execute the submit => Ok, the snackbar appear with an error message.
  • Second, I correct the data and execute a new submit => The submit is succesful, but the success message is not shown
like image 392
optimusfrenk Avatar asked Sep 07 '25 04:09

optimusfrenk


1 Answers

It does not look like you're updating state once it receives new props, only initializing the state the first time it's created.

You could try to update state when it's called a second time.

You could also keep the open state inside the InnerForm component, and add a callback function as a parameter to Notification that would be called in onClose that would update the state of the Snackbar inside InnerForm

Example of the second suggestion:

In 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}/>

In 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}
    />
)
};

Finally, you just have to update message and isOpen in InnerForm where you currently call the Notification function

like image 79
Sodnarts Avatar answered Sep 09 '25 20:09

Sodnarts