I'm trying to create a global alert in my React application that shows up at the top of the screen and every component could use. I have the component GlobalAlert that's used in App.
The problem arises when I'm trying to pass GlobalAlert's setAlert function from any component in the Routes. I tried to use React Refs like so:
const GlobalAlert = React.forwardRef((props, ref) => {
const [variant, setVariant] = useState(null)
const [message, setMessage] = useState(null)
const setAlert = (vrnt, msg) => {
setVariant(vrnt)
setMessage(msg)
setTimeout(() => {
setMessage(null)
}, 5000)
}
useImperativeHandle(ref, () => {
return {
setAlert,
}
})
if (message) {
return (
<div>
<Alert variant={variant}>{message}</Alert>
</div>
)
}
return (
<div>
<br />
<br />
<br />
</div>
)
})
const App = () => {
const alertRef = React.createRef()
const setAlert = (variant, message) => {
alertRef.current.setAlert(variant, message)
}
return (
<div className="container">
<Router>
<Menu />
<GlobalAlert ref={alertRef} />
<Routes setAlert={setAlert} />
</Router>
</div>
)
}
And when I try to use setAlert after backend operation in NewIngredient is finished:
const NewIngredient = ({ setAlert }) => {
...(excluded logic here)...
const submit = async e => {
e.preventDefault()
try {
await addIngredient({
variables: {
name: name.value,
price: Number(price.value),
...(kcal.value && { kcal: Number(kcal.value) }),
},
})
} catch (error) {
console.log('Error adding ingredient in NewIngredient.js', error.message)
}
setAlert('success', `New Ingredient ${name.value} added!`)
resetName()
resetPrice()
resetKcal()
}
return (
<div>
<Form onSubmit={submit}>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control {...name} />
<Form.Label>Price</Form.Label>
<Form.Control {...price} />
<Form.Label>Kcal</Form.Label>
<Form.Control {...kcal} />
<Button type="submit">Add ingredient</Button>
</Form.Group>
</Form>
</div>
)
}
Unhandled Rejection (TypeError): Cannot read property 'setAlert' of null
setAlert
src/App.js:27
24 | const alertRef = React.createRef()
25 |
26 | const setAlert = (variant, message) => {
> 27 | alertRef.current.setAlert(variant, message)
28 | ^ }
29 |
submit
src/Ingredient/Forms/NewIngredient.js:49
46 | console.log('Error adding ingredient in NewIngredient.js', error.message)
47 | }
48 |
> 49 | setAlert('success', `New Ingredient ${name.value} added!`)
50 | ^
51 | resetName()
52 | resetPrice()
Also if I get this to work it requires mindless prop drilling down the component structure.
I'd prefer not using Redux if possible, since I'm using Apollo client to handle all my application's state. I tried to use React Context API, but I found no way of passing the setAlert func for the NewIngredient's submit function.
Is there a clear and easy solution to this?