0

I've architected my modals as suggested by Dan Abramov in this link. In short, he recommends rendering modals at the top level, and passing in the props at the same point that we dispatch an action to show the modal.

This works for the most part, but has one flaw that I cannot seem to overcome: The modal receives props only on the initial dispatch. If those props change at some point while the modal is open, those changes will not be reflected in the modal.

Here is my code:

MainScreen.js

const MainScreen = () => {
  const { list, addListItem } = useListPicker()
  const handleOpenModal = () => {
      dispatch(modalShow('settingsModal', {
        list,
        addListItem,
      }))
  }
  return (
    /* ...some jsx */
    <Button onPress={handleOpenModal} />
  )
}

SettingsModal.js

const SettingsModal = ({ list, addListItem }) => {
  return (
    <List data={list} clickListItem={addListItem} />
  )
}

useListPicker.js (where list is stored)

import produce from 'immer'


const initialState = {
    list: [],
}

const reducer = (state, { type, ...action }) => {

    switch (type) {
        case 'ADD_LIST_ITEM':
            return produce(state, (draft) => {
                const { data } = action
                draft.list.push(data)
            })

        default:
            throw new Error('Called reducer without supported type.')
    }
}

export default () => {

    const [state, dispatch] = useReducer(reducer, initialState)

    const addListItem = (data) => dispatch({ type: 'ADD_LIST_ITEM', data })

    return {
        list: state.list,
        addListItem,
    }
}

As we can see in SettingsModal, we can call the function passed down (addListItem) that will properly add to the list when called (this data lives in a custom hook). However, the SettingsModal has no way to get access to that updated list, since props are passed in only once: when we initially dispatch modalShow.

What is the best way to handle this issue? Would Dan's solution not be appropriate here? Before I refactored my modals to work in a similar way to his post, I rendered each modal in the relevant part of the tree. I refactored this because the same modal would appear elsewhere in the codebase.

Tycholiz
  • 1,102
  • 4
  • 17
  • 33
  • where is the updated list stored? If you have multiple references to it, it should be in your redux store. – Leo Jul 27 '20 at 18:31
  • I updated the post to include the hook where the list is stored. – Tycholiz Jul 27 '20 at 18:35
  • There is really only one part of the codebase where `list` is referenced, so it would seem odd to me to keep it in the store. Is this really the best approach? Seems like nothing would be a "perfect" solution here – Tycholiz Jul 27 '20 at 18:40
  • I assumed that you're using redux, because you linked Dan Abramov's answer on how to do this with redux. I believe a Modal, which is completely decoupled in the component tree from other components, which interacts with the same data is one of the situations in which global state is needed and redux really shines. I'd put the list into the redux store and connect the modal and your MainPage. – Leo Jul 27 '20 at 18:45
  • yes I am in fact using Redux, whose sole responsibility is to determine which modal to render, and what props it should receive (again, only on first render). I should have clarified my position on usage of redux store: this `list` does appear in multiple places throughout the code, but they are different instances of each other. This is why I am hesitant to just keep the list in the store, and instead opted to hold it in a hook where it is more scoped – Tycholiz Jul 27 '20 at 18:54
  • To add clarification to my previous point of being hesitant to use the store: this `list` is a list of categories that an `item` can belong to. As the user is creating the item, he selects the categories (which adds them to the `list`). The problem comes in here: elsewhere in the app, the user can edit the `item`. If we were to put this list in the store, there would be a single instance, and thus the list would bleed between the "creation mode" and the "edit mode" – Tycholiz Jul 27 '20 at 19:20
  • Ok. I think would keep the list (or multiple lists) in the store anyways. (If you are worried about performance, there is an interesting section in the new redux tutorial here: https://redux.js.org/tutorials/essentials/part-6-performance-normalization) – Leo Jul 28 '20 at 12:10
  • If you don't want to store the list in the store, you could also pass a getList() function to the modal (instead of the list itself) which returns the list. You could than either store a copy of the list in the Modals state (resolve it in the useEffect hook) and update it with the getList fn after calling the addItem fn. Or you could just resolve the list in the render function. In that case you must make sure that the component rerenders if the list is updated though. However imo both aren't elegant solutions. – Leo Jul 28 '20 at 12:17

0 Answers0