Problem
I am currently working on UI and I use React. Inside of the .jsx component, I have everything : HTML (JSX), JavaScript-Logic, and API calls. Everything in one file, this gets messy.
Goal
I would like to outsource functionality, so I created a class that should handle all API-Calls. I also would like to use RxJS and combine axios with RxJs.
Code
What is happening in the the code? I have a class ApiCalls that contains a static method putApiCallExample. There I do the api call but creating a Promise with axios. I use the from() functionality form rxjs to create an observable and inside the pipe i return the data.
In the Main.jsx I am using this in the useEffect()-hook, I subscribe on it and set the State based on it.
class ApiCalls:
static putApiCallExample(URL, data){
const promise = axios
.put(URL, data, {
headers: {
"Content-Type": "application/json"
}
});
return from(promise).pipe(map(res => res.data));
}
const Main = () => {
const [show, setShow] = useState(false);
useEffect(() => {
ApiCalls.putApiCallExample().subscribe(
res => {
console.log("1", res);
setShow(true);
},
err => {
console.log("2", err)
}
);
}, [])
}
Question
Can I interpet the subscribe() functionality as same as .then() from axios ?
Do I need to unsubscribe here?
Does this cause performance issues to mix axios and rxjs?
I assume that if you use Axios, you don't need to receive multiple response from the server like for SSE or websocket. So:
Can I interpet the subscribe() functionality as same as .then() from axios ?
In a way, yes, the observable subscribe callback is triggered when Axios promise resolves. Then it will not be triggered anymore, so in this specific case, the RxJs observable behaves the same way as the Axios promise.
Do I need to unsubscribe here?
As the Observable can't be triggered more than 1 time, I don't see any reason to unsubscribe.
Does this cause performance issues to mix axios and rxjs?
You're only wrap Axios promise into a RxJs observable. This RxJs wrapper will not have a significant memory or CPU blueprint.
By the way, this is basically what's Angular Http client is doing internally. My opinion is that it's safe, but it doesn't bring too much value either.
Related
Let’s imagine a simple component run where the user clicks on a button that shows a popup (setState(P)) and that causes both an HTTP (setState(H)) and WebSocket (setState(W)) server to respond with other data to be shown in the popup. Note that onClick, onHTTPResponse and onWSResponse are simple prop event handlers passed to the component which call setState:
onClick: --setState(P)-->
onHTTPResponse: ---setState(H)-->
onWSResponse: ----setState(W)-->
Now let’s imagine another run where WebSocket is faster than the HTTP response:
onClick: --setState(P)-->
onHTTPResponse: ----setState(H)-->
onWSResponse: ---setState(W)-->
Problem with this run is that the WebSocket setState(W) depends on the data returned by the HTTP response, hence it depends on setState(H) — setState(W) must occur after setState(H).
How would I deal with such situation?
Edit: it seems from the responses that I didn’t explain my problem clearly. onHTTPResponse and onWSResponse handlers are called with the response of each. This is the API I have available. I need to solve the setState call order from the usage perspective.
Note also that the idea of these prop handlers is that they’re called at unpredictable times. The same example could be made with subscribing to an event emitter for whichever data. So answers that involve async/await or promises don’t really make sense: I’m dealing with an event emitter kind of API.
Why don't you call them in the same order that you described?
If sending a message via WebSocket needs data from HTTP request you can wait for the request from HTTP and call send a message via Websocket afterwards.
Just an example;
import React, { useEffect, useState } from "react";
const exampleSocket = new WebSocket(
"wss://www.example.com/socketserver",
"protocolOne"
);
export default function App() {
const [httpData, setHttpData] = useState(null);
const [socketData, setSocketData] = useState(null);
const onClick = () => {
fetchData();
};
const fetchData = () => {
fetch("yourAPI")
.then(response => {
// Examine the text in the response
response.json().then(function(data) {
sendMessage(data);
setHttpData(data);
});
})
.catch(err => {
console.log("Fetch Error :-S", err);
setHttpData(err);
});
};
const sendMessage = data => {
exampleSocket.send(data);
};
useEffect(() => {
exampleSocket.onmessage = evt => {
// listen to data sent from the websocket server
const message = JSON.parse(evt.data);
setSocketData(message);
};
}, []);
return (
<div className="App">
<button type="submit" onClick={onClick}>
Send request
</button>
<div>{JSON.stringify(httpData)}</div>
<div>{JSON.stringify(socketData)}</div>
</div>
);
}
use setState callback
this.setState(p, () => this.setState(H, () => this.setState(W)))
setState callback will be triggered with updated state values, so no need to use async/await.
You can use aysnc await function with await in front of onHTTP function and onWebSocket function. Note that you need to put the code you want to run first above the one you want to run later. Example,
await onHTTP()
await onWebSocket()
They second one will only run when the first one is successful.
Since my question was rather unclear I decided to post a more generic one without using React (but pure JavaScript). It seems that using observables is a way to solve this problem. More details are posted in this other question: How to deal with race conditions in event listeners and shared state?
I am learning redux-thunk middleware as a beginner react developper, and I don't understand why dos the function (returned by redux-thunk) returns a promise (returned by fetch())
I tried not to return anything, and it worked, so why do we return it?
export function getCourses() {
return fetch(baseUrl)
.then(handleResponse)
.catch(handleError);
}
export function loadCourses() {
return function(dispatch) {
dispatch(beginApiCall());// dispatch some synchronous action
return courseApi
.getCourses().then(courses => {
dispatch(loadCourseSuccess(courses));
}).catch(error => {throw error;});
};
}
For the component named MyComponent dispatching loadCourses() action
function MyComponent(props){
.......
useEffect(() => {
loadCourses()
});
const mapDispatchToProps = {
loadCourses,
.....
}
}
I think i got the answer(from a colleague).
if you want to chain certain activities, your action would need to return a Promise.
it's just a good way to allow chaining activities after the result is returned!
Well, first of all the function returns some data because you asked it to return some sort of result return function(dispatch) {...}.
If you want to ignore the returned result just remove the return from return function(dispatch) {...}.
Secondly, the function returns a promise because of the way that you have written your call to API functions (wrapped inside promise and not returning callbacks upon function completion).
If you want to get the actual result of the API call you should use the Async / Await syntax.
With a plain basic Redux store, you can only do simple synchronous updates by dispatching an action. Middleware extends the store's abilities and let you write async logic that interacts with the store.
Thunks are the recommended middleware for basic Redux side effects logic, including complex synchronous logic that needs access to the store, and simple async logic like AJAX requests.https://github.com/gaearon/redux-thunk
The thunk middleware knows how to turn thunk async actions into actions, so you just have to have your simple_action() to be a thunk and the thunk middleware will do the job for you, if the middleware see a normal action, he will dispatch this action as normal action but if it's an async function it will turn your async action into normal action.
You can also see return promise from store after redux thunk dispatch
I have a JS design question about how to provide a redux store to files that aren't react components. I have a typical react-redux application. Instead of calling fetch directly from my react components, I currently make all of my service calls in a centralized simple functional utility method file called fetches.js.
const mainFetch(uri, onSuccess, options) {
fetch(uri, options).then(response => {
onSuccess(response);
});
}
const export fetchDogs = (onSuccess) => {
mainFetch('/dogs', onSuccess, { method: 'GET' });
}
const export fetchCats = (onSuccess) => {
mainFetch('/cats', onSuccess, { method: 'GET' });
}
I realized that it'd be useful for my application to know exactly which of these requests we're currently waiting for. So I was thinking of adding this information to my redux state so that I could update mainFetch to look something like:
const mainFetch(uri, onSuccess, options, requestId) {
store.dispatch({type: 'STARTED_REQUEST', request: requestId);
fetch(uri, options).then(response => {
store.dispatch({type: 'FINISHED_REQUEST', request: requestId);
onSuccess(response);
});
}
But there's one problem, fetches.js has no access to the redux store. I could add a 'store' param to all of the methods in fetches.js, however I was thinking there'd be a better JS design pattern. Maybe initializing a class in App.js or something, similarly to how react-redux uses the Provider and 'connect' to provide the store to all child components. I'm new to JS so I was wondering how an experienced JS developer would solve this problem.
This design is lacking a middleware to handle the promises.
The basic idea is when you dispatch a Promise, from the code where you are 'calling' these 'fetch' functions, there would be a middle ware which would take the action dispatched and dispatch other actions such as 'Started fetching' and 'fetching ended' to mark the asynchronous flow.
A good start would be this link on the official site of redux - https://redux.js.org/advanced/asyncflow
A middleware - https://github.com/pburtchaell/redux-promise-middleware
I'm using redux-thunk to manage async stuff in my react application and I want to use redux-observable to manage complex async flow more easily (concat multiple Ajax calls for example). Is there a way to do so without modify what's already done?
Here's an example of what I mean:
const fetchSomeData = () => {
return (dispatch, getState) => {
doAnAjaxCall((err, response) => {
if (err) return dispatch({type: 'ERROR', err})
// do something
return dispatch({type: 'SUCCESS', response})
})
}
}
Is it possible to use fetchSomeData inside an epic?
Since redux-thunk is promise based redux-observable should allow that, am I missing something?
Yep! You totally can use them together. Just place the redux-thunk middleware before the redux-observable middleware.
applyMiddleware(thunkMiddleware, epicMiddleware)
https://stackblitz.com/edit/redux-observable-playground-8c7pd9?file=ping-pong.js
Redux applies middleware in the order they are provided as arguments, so in this case we want the thunk middleware to absorb any dispatched thunks so that the thunk functions themselves never reach redux-observable (only pure actions). But your epics can still dispatch thunks since the redux-observable middleware uses store.dispatch under the hood.
export function postRegister(credentials) {
console.log(credentials);
return dispatch => {
return fetch('/user/register', {
method: 'post',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
}
}
I have few doubts regarding code above.
Can I use export () => {} instead of writing the word function here? Just to stay cleaner.
dispatch is a global variable? I did not see it's imported or required somewhere in the file.
Is specifying headers necessary here? I'm seeing that in every of the api call.
Why there's no catch in this promise call? Overall the code is bad?
No really, you could but you need a name to actually use it in your components.
No, dispatch is a parameter of the arrow function, you can also define getState to access the current redux state. By the way, you can totally assign new names if you want.
It depends on your server, but generally if you are using a JSON API, you would want to send that header.
Yes, overall that code doesn't look good, I would recommend using a middleware to handle the fetch requests, your actions should only send the configurations such as the url, body, method, etc... and your middleware should handle adding common headers (such as the content-type).
You could have an action like this:
export function postRegister(credentials) {
return {
types: [REGISTER, REGISTER_SUCCESS, REGISTER_FAIL],
promise: {
url: '/user/register',
data: credentials,
},
};
}
Something as simple as that, then your middleware should do the fetch and dispatch the action types based on the server response.
If you want to know more about how the middleware should handle the fetch request and dispatch the actions, make sure to take a look at my post here: https://stackoverflow.com/a/39971763/146718
not unless it is export default. since later u will need to import it by name.
no, dispatch is an argument that is passed to your function:
(dispatch) => {}
Totally depends on your application, server, request, etc.
you could add .catch((e) => {}) your self, or use some interceptors for generic errors, do a dipatch from there and add a reducer which will handle these actions. you could read more here:
What is the best way to deal with a fetch error in react redux?