Sequencing two actions together using an epic in redux-observable - javascript

I am building an application with react-native using redux-observable.
Right now I am struggling to do a simple decoration over two actions in redux-observable by using an epic. What I am trying to achieve is simply to map two actions to one.
The error I am getting is:
You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Here is the code I am using:
import { Observable } from 'rx';
export const startApplicationEpic = (action$: Observable) =>
action$
.ofType('START_APPLICATION')
.flatMap(() =>
Observable.of(
{ type: 'REGISTER_PUSH_NOTIFICATIONS'},
{ type: 'AUTHENTICATION_REQUEST' }
)
);
// index.js
store.dispatch({ type: 'START_APPLICATION' })
To me the RxJS code I am using is valid, and if epics are a stream than this should work, or is it only possible to dispatch a single action from an epic?

It appears you're importing rx (which is v4) instead of rxjs (which is v5). So your code is providing a v4 Observable to the flatMap of a v5 Observable (redux-observable internals by default give you v5). The two versions cannot interop by default, but there are temporary interop layers. I admit this is all pretty confusing.
The best way to use v4 with redux-observable is to include the adapter we created for it redux-observable-adapter-rxjs-v4. If if you are using v4 on accident or it otherwise doesn't make a difference, definitely use v5 instead.
Once you fix that, the code as provided works as expected, without errors: https://jsbin.com/xiqigi/edit?js,console

Related

Async dispatch in redux-toolkit

I'm using redux-tookit for state.
My action:
const updateSomething = (data: string) => async (dispatch) => {
await user.set({ data })
dispatch(updatedData(data))
}
In my view I want to do something like:
const dispatch = useDispatch()
await dispatch(updateSomething('Hi!'))
Update 5 July 2021
TL;DR
If typeof store.dispatch still doesn't give you the right typing with ThunkDispatch as one of the overloaded options, you may consider manually typing it, like so: -
export type AppDispatch = ThunkDispatch<AppState, null | undefined, AnyAction> &
Dispatch<AnyAction>;
Note: This is the correct typing with the default middleware, if you have added more middlewares you should try to figure out the possibilities.
Background
While my proposed solution (below) works in codesandbox, it doesn't work in my project, which I ported from vanilla redux to redux toolkit. Maybe some of the packages installed break the types, just a speculation but when I include redux-debounced in my codesandbox sample (link below), the type for store.dispatch is falled back to Dispatch<AnyAction>, even without including redux-debounced in middleware.
This is certainly a mystery that has to be resolved!!
I had the similar issue as TS, so I made a simple project in codesandbox and surprisingly it works with a minor tweak!
In my view, what TS meant is that updateSomething('Hi!') is a valid thunk created using createAsyncThunk() in redux toolkit, where dispatching the thunk should return a Promise. That's a feature in redux toolkit. But unfortunately, somehow typescript is returning AsyncThunkAction and invoking the following line:
await dispatch(updateSomething('Hi!'));
actually yields a typing error. Here's what I got in my codesandbox project:
In my case, fetchDynamicName() is a valid thunk and supposedly the type of dispatchReturn should be a Promise.
With a minor tweak found in this post, it actually works!!
All we need is to export the dispatch type of the store, like so:
export type AppDispatch = typeof store.dispatch;
and assign the type to the dispatch function before using it:
const dispatch: AppDispath = useDispatch();
And voilĂ ! See the screenshot below:
You can take a look at my codesandbox project at https://codesandbox.io/s/fast-cdn-08vpu?file=/src/App.tsx.

Mocking API call in JS

I need to mock API calls for a button click but the actual call is nested down in a utility file that is called by a middleware file. Some framework code was using Jest, axios-mock-adapter, and Enzyme. (I'm still wrapping my head around what each of these do).
So let me preface this. I'm an intern at a company where my task is to test some JS code for a piece of software built on a microservice architecture. So first let me apologize for any improper verbage. My background is in C/C++ and x86 assembly. No, I didn't fudge my resume when applying for this position. The company was fully aware that I had little to no experience with JS. I've attempted to create a mock = MockAdapter('axios') then calling that with mock.OnGet().reply() but when checking my coverage it seems to error every time.
Theres to much code to post so I'll try to give an example
class ComponentName extends component {
stuff
}
ComponentNameFunc {
this.middleware.funcName.then(
response ()=>{}
errorRespone ()={}
)
}
//funcName is a name of a middleware function that calls a function
//in the utility file. The utility file does the axios.get call
When I render the component then simulate a button click it calls this.middleware.funcName but then the coverage shows it going to the errorResponse portion. Heres a test example
describe('test',()=>{
test('button click', done => {
mock.onGet('aURL').reply(200,mockData);
Enzyme.configure({ adapter: new Adapter() });
const wrapper = shallow(
<ComponentName/>);
expect(wrapper.exists()).toBe(true);
wrapper
.find("Button")
.at(0)
.simulate("click");
done();
)};
)};
EDIT: So I found part of the issue. I had multiple mocks for different API calls and apparently only 1 was registering. However, some of these functions that I'm testing will make two API calls. How do I mock up two separate API calls for a single test? Originally I had some thing like this
import axios from "axios"
let mock = MockAdapter(axios);
let mock2 = MockAdapter(axios);
mock.OnGet("URL").reply(200,Data);
mock2.OnGet("URL2").reply(200,DifferentData);
So I figured it out. I was trying to make multiple mock variables (or are they objects?) like mock, mock2, mock3. It seems replicating mock.OnGet.reply with different information works just fine.

Where to put context specific multi-step async logic in Redux

tl;dr I would like to know where to place context specific multi-step async callback logic in a redux architecture, and if I am on the right track with the example code I supply below. By "multi-step" and "context specific" I typically mean server calls initiated by some user action (onClicks, etc) where the logic might only be relevant for a given component (such as redirect to a given route when successful).
The redux docs has this to say on code with side effects:
In general, Redux suggests that code with side effects should be part of the action creation process. While that logic can be performed inside of a UI component, it generally makes sense to extract that logic into a reusable function so that the same logic can be called from multiple places—in other words, an action creator function.
While that seems fine, I am not totally sure whether it is "correct" to put calls to my routing component in there, as these action creators usually seem quite generic, and triggering routing to some other resource in the app is usually quite context dependant.
I also find it a bit weird to put these quite-different beasts, that trigger action creators asynchronously and dispatch the resulting actions, in the same files (foo-model/actions.js) as the "clean" sync action creators. Is this the right place? When reading tutorials on Redux it seems like they live side by side.
The example code is quite simple and basically describes these steps:
On a user click, call a function with some param
This function calls another async function (such as a network call)
When the async call completes, trigger a routing action to another page
Background: I want to gradually refactoring a Meteor project by moving all Meteor specific bits out of the React components, eventually substituting Meteor in the front and back for something else. As there are about 50KLOC I cannot do this in one go, so I am gradually working my way through one route at a time, hoping to end up with a standard React+Redux+ReduxRouter package. In the current code routing, data fetching, and rendering is somewhat intertwined in each component, and I am having some trouble finding out where to put multi-step async logic, such as the example below.
Details on the stack I am trying to wrangle my way out of:
FlowRouter for routing
Meteor/MiniMongo for data mutation and retrieval
React Komposer for Higher Order Components
old Meteor code in MyContainerComponent
// triggered as onClick={(e) => this.saveEncounter(e.target.value)}
// in render()
const saveEncounter = (encounter) => {
Meteor.call('createEncounter', encounter, handleSaveResult);
}
};
const handleSaveResult = (err, encounterId) => {
if (err) {
this.setState({errorMessages: err});
} else {
// route to another page
NavigationActions.goTo('encounter', {encounterId: this.props.encounter._id || encounterId});
}
}
new redux code - moved into actions.js
I am trying to keep the implementation straight forward (no additional deps) at this point to understand the foundations. "Simplification" using redux-thunk, redux-actions or redux-saga need to come later. Modeled after the example code in the Redux tutorial for Async Actions
export const saveEncounter = (encounter) => {
function handleSave(err, encounterId) {
if (err) {
dispatch(createEncounterFailure(err), encounter);
} else {
dispatch(createEncounterSuccess(encounterId));
}
}
dispatch(createEncounterRequest(encounter));
Meteor.call('createEncounter', encounter, handleSave);
}
// simple sync actions creators
export const CREATE_ENCOUNTER_REQUEST = 'CREATE_ENCOUNTER_REQUEST';
function createEncounterRequest(encounter) {
return {
type: CREATE_ENCOUNTER_REQUEST,
encounter
};
}
export const CREATE_ENCOUNTER_FAILURE = 'CREATE_ENCOUNTER_FAILURE';
function createEncounterFailure(error, encounter) {
return {
type: CREATE_ENCOUNTER_FAILURE,
error,
encounter
};
}
export const CREATE_ENCOUNTER_SUCCESS = 'CREATE_ENCOUNTER_SUCCESS';
function createEncounterSuccess(encounterId) {
return {
type: CREATE_ENCOUNTER_SUCCESS,
encounterId
};
}
As you noted in a comment, Dan Abramov discussed a lot of the ideas behind handling async work in Redux in his answer for how to dispatch an action with a timeout. He also wrote another excellent answer in why do we need middleware for async flow in Redux?.
You might want to read through some of the other articles in the Redux Side Effects category of my React/Redux links list to get a better idea of ways to handle async logic in Redux.
In general, it sounds like you may want to make use of either "sagas" or "observables" for managing some of your async logic and workflow. There's a huge variety of Redux middlewares for async behavior out there - I summarized the major categories and most popular libraries in my blog post The Tao of Redux, Part 2 - Practice and Philosophy. There's also some interesting thoughts on a very decoupled saga-based Redux architecture in a post called Redux Saga in Action.
I see your point, you would like to have a way to divide and categorize your actions, is that right? Actions that will execute sync code, async code, logger, etc.
Personally, I use some naming convention. If I have to dispatch an action that has to fetch some data, I call it REQUEST_DATA. If have to store some data arrived from the server to the ReduxStore, I call it STORE_DATA.
I don't have a specific pattern. I also have to point that I divide my codebase based on the feature, so the modules where I define my actions are pretty small and neat
In my experience with Redux, I haven't found any problems with putting async calls inside action creators. I think redux-thunk or some other middleware is very helpful, even for a simple setup.
The only thing I'd add is that I don't find your sample code very readable.
Personally I've come to like the ducks pattern, but also just keeping action types, action creators and reducers in separate files would work to improve clarity.
Hope this helps.

How do I improve Redux's performance when updating fast-moving UI state?

The details of my problem involve me integrating a D3 force graph with my redux state. Each tick update dispatches an action to update a position collection in my redux state. These positions are then merged with node data and use to update my visualization. React handles the DOM and D3 is used basically just to calculate forces, collision detection, etc.
I'm finding it difficult/impossible to maintain a smooth experience for the user using this design pattern. Locally on my laptop, I'm getting ~117ms between actions, well below 60fps (16ms between actions).
I've tried to simplify and streamline my middleware as much as possible to reduce latency.
What other strategies can I employ to get better update time using redux? Or am I trying to do something Redux was never meant to do?
On each tick means requestAnimationFrame right? If not, use it. :)
If you are only dispatching one action, meaning one Redux update -> one change callback -> one React re-render, there's not much you can do, your calculations take too much or your React component tree is too big and non efficient (no shouldComponentUpdate and so on).
In case you're dispatching more than one action on each frame, you may find useful one technique I've used in the past, which is wrapping my reducer so it can handle an array of actions at once. That way you can avoid re-renders until the last one. The code is surprisingly simple, as everything with Redux is:
function withBatching(originalReducer){
return function(state, action){
if(action.type === 'BATCH' && Array.isArray(action.payload)){
return action.payload.reduce(state, originalReducer)
} else {
return originalReducer(state, action)
}
}
}
//now wrap any (or all) of your reducers
const batchedAppReducer = withBatching(myCombinedAppReducer)
In your action creator, instead of dispatching N actions, dispatch one batch of them:
{
type: 'BATCH'
payload: [
{ type: 'MY_ACTION_TYPE', payload: 'xxxx' },
{ type: 'ANOTHER_THING', payload: 'xxxx' }
]
}
If you want to batch actions handled by different reducers, I would add batching on your combined reducer, just prior to createStore. Otherwise you can simply enable batching for your specific need.
I've used this technique when prototyping some games with great success: in my case I had to update multiple entities at once but didn't want to render until the "world" state was updated, so I batched all that updates.
Hope it helps, if you provide some details on your "update" cycle logic I'll try to help you further.
If you are using react-redux, there is also a batch API: https://react-redux.js.org/api/batch
From the doc:
import { batch } from 'react-redux'
function myThunk() {
return (dispatch, getState) => {
// should only result in one combined re-render, not two
batch(() => {
dispatch(increment())
dispatch(increment())
})
}
}

Using mern.io scaffolder tool - What is the .need method all about?

Based on the scaffolder mern.io I was going through the code to see what was going on. I stumbled upon a .need method which looks like something related to es6 classes. I can't seem to find any usable info anywhere, so I ask what is the .need method?
class PostContainer extends Component {
//do class setup stuff here
}
PostContainer.need = [() => { return Actions.fetchPosts(); }];
You can get the project up and running very easily with these commands.
npm install -g mern-cli
mern YourAppName
The mern documentation is pretty terse when it comes to explaining this.
fetchComponentData collects all the needs (need is an array of actions that are required to be dispatched before rendering the component) of components in the current route. It returns a promise when all the required actions are dispatched.
Reading through the code is a much clearer way of finding out what's going on here.
Overview
It's a way to specify some actions that should be dispatched before rendering the component.
This component maps the posts property from the Redux store to a prop called posts so that it can render the list of posts.
// PostContainer.jsx
function mapStateToProps(store) {
return {
posts: store.posts,
};
}
However, initially this property will be empty because the posts need to be fetched from an asynchronous API.
// reducer.js
// initial state of the store is an empty array
const initialState = { posts: [], selectedPost: null };
This component needs the posts to be available before it renders, so it dispatches the action returned from the call to Actions.fetchPosts().
// actions.js
export function fetchPosts() {
return (dispatch) => {
return fetch(`${baseURL}/api/getPosts`).
then((response) => response.json()).
then((response) => dispatch(addPosts(response.posts)));
};
}
When the action has finished dispatching, the store's data can be mapped to the connected component.
Caveat
This isn't a universal way to specify asynchronous dependencies for React components. It only works because mern has a utility method called fetchComponentData that it calls at the server side, in order to populate the Redux store before rendering.
// server.js
fetchComponentData(store.dispatch, renderProps.components, renderProps.params)
This method traverses the components from the second argument to extract the needs from each. Then it executes 'needs` and waits for all the promises to complete.
// fetchData.js
const promises = needs.map(need => dispatch(need(params)));
return Promise.all(promises);
When the promise returned by Promise.all(promise) completes, the Redux store will be populated and the components can safely render their data to be served to the client.
Syntax
You mentioned that you thought it might be related to ES6 classes, so I'll cover the syntax quickly too.
ES6 classes can't have static properties specified in the class literal, instead we have to declare them as properties on the class after it has been defined.
The needs property must be an array of functions that return promises to work with fetchComponentData. In this case we have an arrow function declared inside an array literal. It might help to look at it split up into separate variables.
const fetchPosts = () => { return Actions.fetchPosts() };
const needs = [fetchPosts];
PostContainer.need = needs;

Categories