I'm working on a React application that uses the following architecture:
redux
typesafe-actions
redux-observable
My question is: How can I execute an UI action on specific redux action?
For example, suppose we have the following async actions defined with typesafe-actions
:
export const listTodo = createAsyncAction(
'TODO:LIST:REQUEST',
'TODO:LIST:SUCCESS',
'TODO:LIST:FAILURE',
)<void, Todo[], Error>();
An Epic will watch for listTodo.request()
and send the API call, then convert the response to a listTodo.success()
action. Then the redux reducer will be triggered by listTodo.success()
action and store the todo list into redux store.
In this setting, suppose I want to do the following things in an ponent:
- dispatch a
listTodo.request()
action to retrieve all the actions - After the async request is done (i.e. after
listTodo.success()
action appears in the action stream), redirect the UI to a second path
So my question is, how could I watch the action stream and react to the listTodo.success()
action?
UPDATE: To avoid being too specific, we can think of another case. I want to simply display an alert with window.alert()
after listTodo.success()
appears in the action stream. Or simply console.log()
, or whatever that changes local state (instead of global redux state). Is there a way to implement that?
UPDATE 2: There is a similar question here, but for Angular w/ ngrx. What I want to do is exactly the thing described in above post, but in React / redux-observable fashion:
import { Actions } from '@ngrx/effects';
@Component(...)
class SomeComponent implements OnDestroy {
constructor(updates$: Actions) {
updates$
.ofType(PostActions.SAVE_POST_SUCCESS)
.takeUntil(this.destroyed$)
.do(() => /* hooray, success, show notification alert ect..
.subscribe();
}
}
I'm working on a React application that uses the following architecture:
redux
typesafe-actions
redux-observable
My question is: How can I execute an UI action on specific redux action?
For example, suppose we have the following async actions defined with typesafe-actions
:
export const listTodo = createAsyncAction(
'TODO:LIST:REQUEST',
'TODO:LIST:SUCCESS',
'TODO:LIST:FAILURE',
)<void, Todo[], Error>();
An Epic will watch for listTodo.request()
and send the API call, then convert the response to a listTodo.success()
action. Then the redux reducer will be triggered by listTodo.success()
action and store the todo list into redux store.
In this setting, suppose I want to do the following things in an ponent:
- dispatch a
listTodo.request()
action to retrieve all the actions - After the async request is done (i.e. after
listTodo.success()
action appears in the action stream), redirect the UI to a second path
So my question is, how could I watch the action stream and react to the listTodo.success()
action?
UPDATE: To avoid being too specific, we can think of another case. I want to simply display an alert with window.alert()
after listTodo.success()
appears in the action stream. Or simply console.log()
, or whatever that changes local state (instead of global redux state). Is there a way to implement that?
UPDATE 2: There is a similar question here, but for Angular w/ ngrx. What I want to do is exactly the thing described in above post, but in React / redux-observable fashion:
import { Actions } from '@ngrx/effects';
@Component(...)
class SomeComponent implements OnDestroy {
constructor(updates$: Actions) {
updates$
.ofType(PostActions.SAVE_POST_SUCCESS)
.takeUntil(this.destroyed$)
.do(() => /* hooray, success, show notification alert ect..
.subscribe();
}
}
Share
Improve this question
edited Mar 27, 2019 at 19:10
charlee
asked Mar 27, 2019 at 18:55
charleecharlee
1,3691 gold badge12 silver badges22 bronze badges
7
-
How do you do navigation in your app? Are you using a redux based router like
connected-react-router
? – Harald Gliebe Commented Mar 27, 2019 at 19:02 -
@Harald I don't want this question to be too specific. I know if the route is stored as redux state this problem can be solved. What if, say, I want to simply show a dialog with
window.alert()
afterlistTodo.success()
is observed? – charlee Commented Mar 27, 2019 at 19:05 -
Then either create a new epic that watches for
listTodo.success()
, perform a side effect withaction$.tap(() => {window.alert()})
and thenignoreElements()
– Harald Gliebe Commented Mar 27, 2019 at 19:10 - Thanks @Harald. Can I do this (create an epic) in a ponent? – charlee Commented Mar 27, 2019 at 19:11
-
You need to use
bineEpics()
to bine all the Epics in your application and run it with theepicMiddleware
. See redux-observable.js/docs/basics/SettingUpTheMiddleware.html how to set upredux-observable
correctly – Harald Gliebe Commented Mar 27, 2019 at 19:14
4 Answers
Reset to default 1With redux the ponents update based on state.
If you want to update a ponent based on an action than you update the state in the reducer, such as setting {...state, success: true} in the reducer. From there you simply read the state into your ponent as you normally would and if the state is changing to success than you show your window.
I feel like a dialogue should be a side effect, so you'd put them in epics
Nowadays, the best way to listen for actions inside a ponent is using the createListenerMiddleware
that is included with the redux-toolkit (RTK), bined with useEffect()
:
useEffect(() => {
// Could also just `return dispatch(addListener())` directly, but showing this
// as a separate variable to be clear on what's happening
const unsubscribe = dispatch(
addListener({
actionCreator: todoAdded,
effect: (action, listenerApi) => {
// do some useful logic here
},
})
)
return unsubscribe
}, [])
Docs: https://redux-toolkit.js/api/createListenerMiddleware#adding-listeners-inside-ponents
I usually use Epic Observables for side effects, and use a selector for state changes in ponents, but there are certain situations where listening for an action directly inside a ponent (instead of a state change) is the best solution. The above fits that bill perfectly.
Might be a little late but I solved a similar problem by creating a little npm module. It allows you to subscribe to and listen for redux actions and executes the provided callback function as soon as the state change is plete. Usage is as follows. In your ponentWillMount or ponentDidMount hook:
subscribeToWatcher(this,[
{
action:"SOME_ACTION",
callback:()=>{
console.log("Callback Working");
},
onStateChange:true
},
]);
Detailed documentation can be found at https://www.npmjs./package/redux-action-watcher