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.