Closed bastoune closed 5 years ago
Do you have some code that demonstrates what you're trying to do? The mechanism behind react-alert is that it passes an alert object from the Provider to a component via a prop, so the alert can only be used inside a component.
One use case of this is to call it outside in a helper module. Let's say I have an http.js
helper module which I call fetch and also check 400/500 statuses. When I use this library for http calls , I can always use it to display this alert whenever I encounter an error.
@geocine, do you use any state management library? From my point of view, you need to have a mediator class, if to take Redux or Mobx - it will be Store. It can control the http requests, catch errors and contain an observable value on which the AlertComponent will react as soon as value changes. What do you think?
@besLisbeth unfortunately the code I am maintaining doesn't have a Store, but regarding that point I am looking for a way to actually trigger the alert from the mediator class, not on the component level.
@geocine, then I think the only way is to store the reference to the alert provider in your mediator class like that
//Component.jsx
this.alert = React.createRef();
render() {
return <AlertProvider ref={this.alert} />;
}
//MediatorClass.js
setAlertRef(ref){
this.alertRef = ref;
}
showSuccessAlert(){
this.alertRef.success(); //or show(), or error(), so on
}
Hope, it will help
@besLisbeth I will try it out as soon as I can thanks again.
@besLisbeth , I was able to let the alert show using your suggestion, by doing this
import React from 'react';
export class AlertRef {
static instance = null;
static createInstance() {
var object = React.createRef();
return object;
}
static getInstance () {
if (!AlertRef.instance) {
AlertRef.instance = AlertRef.createInstance();
}
return AlertRef.instance;
}
}
import { AlertRef } from './helpers/alert'; // reference to singleton AlertRef
class Root extends Component {
render () {
return (
<AlertProvider template={AlertTemplate} {ref={AlertRef.getInstance()}>
<App />
</AlertProvider>
)
}
}
import { AlertRef } from './helpers/alert'; // reference to singleton AlertRef
export const getHttp = (path) => {
return fetch(`${path}`, {
headers: getHeaders()
}).then(response => checkStatus(response))
.catch(error => AlertRef.getInstance().current.show(error.message))
}
This is working however I have the alert showing endlessly
Any thoughts how to do this correctly?
Hi, @geocine, it looks like the problem is not in the code which you attached. An alert showing is the result of an error happened in a network request and the error from it showed in console endlessly. I think, that an error somewhere in the calling the network request method, can you check it by yourself once more?
@geocine, also, do you really need the singleton class AlertRef? Maybe it can be simplified into just storing and updating (if needed) refs?
@besLisbeth , I am purposely throwing a 404, so that the alert will show. I will use it specifically for this use case. If I do not add the AlertRef
, the 404 requests only fires once and an alert will not show.
@geocine, I just ran your code from an example and it worked for me. Can you please deploy the code with an endless loop somewhere in the sandbox? It still looks to me like the problem not in the AlertRef itself.
@besLisbeth you can try my repo . https://github.com/geocine/react-auth-demo
Continued in geocine/react-auth-demo#1
Closing this as @besLisbeth helped already. Thank you, BTW!
@geocine's example no longer works in v5 because the Provider is no longer a class which prevents React from using Refs. Downgrading to 4.0.4 works.
Hi, @RyanBertrand! Can you please explain a little bit how did you attempt to use a reference to the alert to find the solution to the problem? Do you use mobx, redux or smth else?
I think that in most cases using a reference from useAlert() hook will work.
@besLisbeth I needed to show an alert outside of a component. The new hooks functionality is great but it does not work in class components which is what all of our components are.
From (Hooks FAQ)
You can’t use Hooks inside of a class component
Here is how I use refs to show alerts anywhere in the app (not just components).
Alerts.js
import React from 'react';
export default class Alerts {
static _reactAlertRef = React.createRef();
static getRef(){
return this._reactAlertRef;
}
static show(type, message){
let reactAlert = Alerts.getRef().current;
let showFn = reactAlert[type];
showFn(message);
}
static showInfo(message){
this.show('info', message);
}
static showSuccess(message){
this.show('success', message);
}
static showError(message){
this.show('error', message);
}
}
AppWrapper.js
import Alerts from './Alerts';
class AppWrapper extends Component {
render() {
let alertRef = Alerts.getRef();
return (
<AlertProvider ref={alertRef}
template={AlertTemplate}>
<App/>
</AlertProvider>
);
}
}
export default AppWrapper;
Now from anywhere in the app, we can show an alert by calling:
Alerts.showError("Oops!");
This allows us to show alerts from our Redux (thunk) actions without components needing to know what is going on.
@RyanBertrand, thank you a lot for the detailed explanation.
So, yes, it's clear that Hooks cannot be used outside of a class component. But the value which you get from the hook useAlert
or HOC withAlert
can be used as in your example.
I think that we have a misunderstanding here:
Here is how I use refs to show alerts anywhere in the app (not just components).
Anyway, because alerts themselves are components, they should be managed at the component level. Another question is how you can archive this.
My proposal of how you can use version 5 of the library:
You can get the reference from useAlert
hook or withAlert
HOC in
Structure <App/>
component this way:
<Alerts>
<EverythingElse/>
</Alerts>
And use <Alerts/>
component as Consumer for AlertProvider
- get the reference to alerts from useAlert
hook or withAlert
HOC and wait for dispatching the actions. Basing on the code above, I propose, that you will have such actions as SHOW_SUCCESS/INFO/ERROR
. Then, when something will hapen in your aplication, you will dispatch the needed action, and component <Alerts/>
will wait for them.
What do you think, the second option will meet your needs, or it is totally unacceptable?
@RyanBertrand thank you! Your answer worked great for me. My React app is running on a robot so it's always getting alerts about getting stuck or having some type of hardware problem. I ran into an issue where when the robot is stuck, instead of saying "I'm stuck once", it says it constantly until the issue is fixed (pub/sub with lots of pubs). I modified your answer to include this:
static show(type, message) {
const reactAlert = Alerts.getRef().current;
const showFn = reactAlert[type];
// if new message is already in the current list of alerts
if (message && reactAlert.state.alerts && !(reactAlert.state.alerts.some((alert) => (
alert.message === message
)))) {
showFn((typeof message === 'string' || typeof message === 'number') ? message : JSON.stringify(message));
}
}
^^ In case anyone else runs into this.
@RyanBertrand This doesn't work any more?
I got Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
.
I'm using latest version of react-alert, which is v7.0.2.
Does your solution still work if you update react-alert?
After looking into the source code of react-alert, I think maybe we cannot passing ref anymore.
I would like to call the opening of an alert outside of a component (for example because the component will be unmounted or so)
But I can't find how to import the right functions ?