4

Im learning React and using the new implemented "Hooks" from Documentation. I got a problem with a Modal (Dialog from Material UI) and the open/close function using useEffect() function.

I have already read these both articles: React.useState does not reload state from props and How to sync props to state using React hook : setState()

It's already helped me, I have forgot to use the useEffect() function instead I was just setting the useState from what comes from props. Learned that useState will be executed only one time for setting the initial state. But I have however more one problem.

function AddItemModal(props) {
  const [open, setOpen] = useState(props.isOpen);

  useEffect(() => {
    setOpen(props.isOpen);
  }, [props.isOpen]);

  function handleClose() {
    setOpen(false);
  }

That works for the first time when I click at the add button (in another Component) and handle the click (it changes the state to true) and I pass it as props to the Modal. But when I click (in modal) at close and try to click at add to open it again, nothing happens. In case needed, here's the code from the component where I handle the click and call the modal.

function ItemsTable() {
  const [addModalOpen, setAddModalOpen] = React.useState(false);

  const handleAddClick = () => {
    setAddModalOpen(true);
  };

  <Box mt={4} position="fixed" bottom={10} right={10}>
    <Fab color="secondary" aria-label="Add" onClick={handleAddClick}>
      <AddIcon />
    </Fab>
  </Box>
  <AddItemModal isOpen={addModalOpen} />
Rodrigo Schneider
  • 317
  • 2
  • 5
  • 17
  • Are you sure that ItemsTable is re-rendering when you expect it and addModalOpen is what you expect it to be? – Dominic May 28 '19 at 12:40
  • Hello Dominic, tried some console.logs: ItemsTable.js:39 addModalOpen state: false ItemsTable.js:42 handling click at add button and setAddModalOpen ItemsTable.js:39 addModalOpen state: true – Rodrigo Schneider May 28 '19 at 12:50
  • Hmm strange as your code looks ok, the effect should run and change the state if the prop changes – Dominic May 28 '19 at 12:59
  • 1
    Hello Dominic, more on debugging, I saw that the Table is the problem. As the modal closes (handle in Modal component) the table is not re-rendered and than is the isOpen always true in table and after click at button nothing happens to the state – Rodrigo Schneider May 28 '19 at 13:02

2 Answers2

3

You have split your modal state across two components which is confusing and going to lead to bugs like this. You should put the state in one place and update it where necessary. In this case, you could keep the modal state in the ItemsTable and pass in a handler for the modal to access.

Something like:

function ItemsTable() {
  const [addModalOpen, setAddModalOpen] = React.useState(false);

  const handleAddClick = () => {
    setAddModalOpen(true);
  };
  const handleClose = ()=>{
    setAddModalOpen(false)
  }
  <Box mt={4} position="fixed" bottom={10} right={10}>
    <Fab color="secondary" aria-label="Add" onClick={handleAddClick}>
      <AddIcon />
    </Fab>
  </Box>
  <AddItemModal isOpen={addModalOpen} handleClose={handleClose}/>
Will Jenkins
  • 9,507
  • 1
  • 27
  • 46
  • Hello Will. Wow! Perfect. Well, I was close haha. My error was to have the handleClose in Modal not in Tables. Thanks for the tip! Just to know if it's right to do so (it works, I wanna just know if it's common way to do it) in Modal I use now: open={props.isOpen} onClose={props.handleClose} – Rodrigo Schneider May 28 '19 at 13:06
  • Yes this pattern of inverting control is the recommended way ^ – Dominic May 28 '19 at 13:11
1

I am not sure if I understood exactly what are you trying to do, but I see that useEffect does not use the state. In order useEffect to be called more than one times, you need to pass the state to it, so your [props.isOpen] needs to change to [open]

Marios Simou
  • 181
  • 3
  • 8
  • Hello Marios! Thanks for your reply. So, I have tried as you said: setOpen(props.isOpen) still the same and here: [open] instead of props. And now the Dev Console shows the following warning: React Hook useEffect has a missing dependency: 'props.isOpen'. Either include it or remove the dependency array. If 'setOpen' needs the current value of 'props.isOpen', you can also switch to useReducer instead of useState and read 'props.isOpen' in the reducer react-hooks/exhaustive-deps – Rodrigo Schneider May 28 '19 at 12:43
  • And sorry if you can't understood, I tried to be clearly as possible. In resume: there's a Component (that works as a table for showing the results) and there's a button there to add a new record to it. These button calls the modal. – Rodrigo Schneider May 28 '19 at 12:44
  • 1
    React follows a top-down architecture, which means that when the state change, the component updated only itself and its children components. BY adding useEffect you were updating only the AddItemModal component without notifying the parent one. Using callbacks, as the suggested answer, handles the state from the parent component and allow to do the changes to the children. I hope that one to be helpful. – Marios Simou May 28 '19 at 13:13
  • Hi Marios. Upvote for that :) thanks for explaining. Got that! – Rodrigo Schneider May 28 '19 at 13:20