react-query within a class component where we have send/receive functions - javascript

I want to use React Query with a specific code structure, and I'd like to know if this is possible for me.
I have a React class component where sending the request and receiving the response are done in separate places
example:
class Engine{
sendMessage(from, message){
...
// for each subscriber
subscriber.receive(from, message)
}
listenFor(subscriber, chanel){
...
}
}
class ComponentA extends React.Component{
messageId;
constructor(props){
...
Engine.listenFor(this, 'APIResponse');
this.state = { data: {} }
}
componentDidMount(){
fetchData();
}
fetchData(){
let msg = createAPIRequestMsg({...});
this.messageId = msg.id;
Engine.send(msg);
}
recieve(from, message){
if(message.id == this.messageId){
let respones = message.response;
this.setState({ data: response });
}
}
}
Now I want someway to use react-query within this structure, I know we can use it within a class in different ways, but my question is how to use it when the function that is supposed to return a response is not returning the response, for example fetchData will not return the response, it will just send a message, and the receive function is the one responsible to get the response back, so in this structure we can't use the fetchData with useQuery, is there a workaround that?

Related

Angular Apollo Set watchQuery Results to a Usable Variable

New to Angular/Apollo/TS and this is driving me nuts, so any help is appreciated.
I am working on setting up a small app with Angular 10, Apollo, and a GraphQL API. I recently built the same thing in Vue and thought recreating the project would be a good way to pick up some Angular.
My connection to the API is working, as is my query, but I can't figure out how to map the results to an array so I can access them in my component. Using console.log inside the subscription shows the correct data is returned. console.log outside of the query on 'this' shows the query results, however they are never saved/mapped to the variable they should be set to.
Here's the code for my service:
import { Injectable } from '#angular/core';
import { Apollo } from 'apollo-angular';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import gql from 'graphql-tag';
const USER_SEARCH = gql`
query getUsers {
search(query: "moose", type: USER, first: 10) {
nodes {
... on User {
login
email
location
name
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
userCount
}
}`;
export class UserService {
loading: boolean = true;
users: [];
constructor(private apollo: Apollo) { }
getUsers(): any {
this.apollo.watchQuery<any>({
query: USER_SEARCH
})
.valueChanges
.subscribe(({ data, loading }) => {
this.loading = loading;
this.users = data.search;
});
console.log(this);
return this.users;
}
}
I can call the getUsers() function from my component, and 'this' has the service listed, and inside of it 'users' has my query results listed in it. However, console.log for this.users in the service or the component returns undefined.
I've tried about every type of example I could find, including the query examples from the apollo docs, and the example of using apollo with angular from hasura.io. Tried using a pipe and map, pluck, just valueChanges, a few different subscribes, setting a variable inside the function to assign the data value to, setting the query to variable, setting the query in ngOnInit in the component, and a few other things I'm sure I'm forgetting. Nothing seems to work. I looked into using a callback to wait for the query to return before setting the value, but my understanding is that I shouldn't have to do anything like that. I'm sure it's something dumb I'm missing or don't know about with Apollo or Angular, but I'm just not positive what it is I'm missing.
Any ideas?
this.getUsers = this.getUsers.bind(this);
within a constructor?
using setTimeout is not an ideal solution, you can directly update your component variable in subscribe callback function and do whatever you want to do with it in your template. Look at my example
getItems() {
this.apollo
.watchQuery({
query: this.getItemsQuery,
})
.valueChanges.subscribe((result: any) => {
this.items = result?.data?.items;
});
}
and in template
<mat-option *ngFor="let item of items" [value]="item.price">
{{ item.name }}
</mat-option>
Maybe not the ideal solution, so I'm still open to trying other things, but I was able to get the value set in my component by using a promise with a timer in the service, then an async await in the component.
Service
getUsers(): any {
return new Promise((resolve, reject) => {
let me = this;
this.apollo.watchQuery<any>({
query: USER_SEARCH
})
.valueChanges
.subscribe(({ data, loading }) => {
this.loading = loading;
this.users = data.search;
});
setTimeout( function() {
if(me.users !== 'undefined'){
resolve(me.users)
}
}, 1000)
})
}
Component
async getUsers(): Promise<any> {
this.users = await this.userService.getUsers();
console.log(this.users);
}
This allows this.users to be set from the service. As far as I can tell, Apollo is still running the query when Angular starts setting values, resulting in the value originally being shown as undefined, but my service having values from the query in the console. Not sure if there's a better way with Apollo or Angular to resolve this issue, but if so I'd love to hear about it.
Thanks!

Reactjs - Why can't i set state?

Hi i'm trying to fetch a user data from jsonplaceholder and update my state with that data. I had no problem fetching the data and logging it to the console. But when i try to setState, i still get an empty object.
I appreciate any help. Thanks.
This is my code:
class ProfilePage extends React.Component {
state = {
profileDetails: {},
};
componentDidMount() {
this.fetchDetails();
}
fetchDetails = async () => {
const baseUrl = "https://jsonplaceholder.typicode.com";
const pathname = this.props.history.location.pathname;
const response = await fetch(`${baseUrl}${pathname}`);
const data = await response.json();
console.log(data); // I can see the data i want here in the console.
this.setState = { profileDetails: data };
console.log(this.state.profileDetails); // I get an empty object here.
};
render() {
return <h1>Name: {this.state.profileDetails.name}</h1>;
}
}
export default ProfilePage;
Thanks everyone for taking the time to answer. Apparently i used setState wrong and missed the fact that it's asynchronous.
From docs of setState
React does not guarantee that the state changes are applied
immediately.
If you want to use up-to-date data, use callback argument (and use it as function, instead of assignment, because it is a method, not a property)
this.setState({ profileDetails: data }, () => {
console.log(this.state.profileDetails)
})
Change this
this.setState = { profileDetails: data };
console.log(this.state.profileDetails);
into this
this.setState({ profileDetails: data });
Put console.log(this.state.profileDetails); inside render for you to see your new state.
setState is a function that recieves data as parameters.
but you use it like setState is a json object
setState - is a method.
Please change code like this - this.setState({ profileDetails: data });
The right way to set state is this,
this.setState({ profileDetails: data })
You have to set state by this way only.
Give a condition for check the data is available or not:-
if(data)
this.setState = ({ profileDetails: data });

Axios ReactJs Laravel: How to retrieve the multiple request Api

I create new laravel project and I combine the React Javascript Framework.
I watch youtube and read some website, how to use the Axios HTTP Requests API.
however they not discuss how to use the axios in retrieving the multiple request api.
Question:
How to retrieve multiple request api and also I want to display it to my render function
I create two public function (Mission,Store) in my Home Controller.
public function mission() {
$content_mission = DB::table('content_structure')
->where('content_pages','=','Home')
->where('content_section','=','Mission-Vision')
->where('status','=','Active')
->orderBy('content_id','Desc')
->limit(1)
->get();
return response()->json($content_mission);
}
public function store() {
$content_store = DB::table('content_structure')
->leftJoin('content_upload_assets','content_structure.content_id','=','content_upload_assets.cid')
->where('content_pages','=','Home')
->where('content_section','=','Store')
->where('status','=','Active')
->orderBy('content_id','Desc')
->limit(1)
->get();
return response()->json($content_store);
}
I also create Index.js to my component folder.
constructor() {
super();
this.state = {
}
}
componentWillMount() {
this.setState({
missionsApiRes: []
});
Promise.all([
axios.get('/api/mission'),
axios.get('/api/store')
]).then(response => {
const [storesApiRes, missionsApiRes] = response;
this.setState({storesApiRes,missionsApiRes}, () => {
});
})
}
renderMission() {
return this.state.missionsApiRes.map(mission =>
<div>{mission.content}</div>
)
}
My Render Function :
<div className="container">
{this.renderMission()}
</div>
Error:
Uncaught TypeError: this.state.missionsApiRes.map is not a function
From quick glance I would say that your this.state.missionsApiRes is not defined at the time of calling render. Try to move it to the constructor:
constructor() {
super();
this.state = {
missionsApiRes: []
}
}
You need custom resolve data of axios promise
Promise.all([
axios.get('/api/mission'), => axios.get('/api/mission').then(data => data)

typescript/angular pass value out of this?

So in normal javascript if I wanted to assign a value to a variable and then use that value outside of a function it would be done by declaring the variable first and then define it's value in the function. I'm brand new to typescript and angular so I am missing how to do this.
In the code below I am trying to get the value from a method in a service and then pass that value into my return. (I hope that makes sense). However I keep getting undefined on console.log(url) with no other errors.
emailsAPI() {
let url: any
this.apiUrlsService.urlsAPI().subscribe(
data => {
this.results = data
url = this.results.emails
}
);
console.log(url)
return this.http.get('assets/api/email_list.json')
}
api-urls service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
#Injectable()
export class ApiUrlsService {
constructor(
private http: HttpClient
) { }
urlsAPI () {
return this.http.get('assets/api/api_urls.json')
}
}
That's because you're calling async method subscribe and then trying to log the coming value before subscription is resolved. Put last two statements (console.log and return) inside the curly braces just after assigning this.results.emails to the url variable
emailsAPI(): Observable<any> {
let url: any
return this.apiUrlsService.urlsAPI()
.flatMap(data => {
this.results = data
url = this.results.emails
// you can now access url variable
return this.http.get('assets/api/email_list.json')
});
}
As per reactive programming, this is the expected behaviour you are getting. As subscribe method is async due to which you are getting result later on when data is received. But your console log is called in sync thread so it called as soon as you are defining subscribe method. If you want the console to get printed. Put it inside your subscribe data block.
UPDATE:
As per your requirement, you should return Subject instead of Observable as Subject being data consumer as well as data producer. So it will consume data from httpget request from email and act as a producer in the method from where you called emailsAPI method.
emailsAPI(): Subject<any> {
let emailSubject:Subject = new Subject();
this.apiUrlsService.urlsAPI()
.flatMap(data => {
this.results = data
return this.results.emails;
}).
subscribe(url=> {
this.http.get(your_email_url_from_url_received).subscribe(emailSubject);
});
return emailSubject;
}
The subject can be subscribed same as you will be doing with Observable in your calee method.

Mobx-React store property undefined on first render, but async loading variable passes

I'm managing my state using Mobx. I call an action for an http request to load pictures, then update the pictures property, then update the loading property. When I load the component that makes the call and console.log the store properties, the loading property is updated, but the picture property is still undefined. It's not until the second render of the component that the picture property is defined Here's an example:
export class PhotoStore {
#observable picInfo = []
#observable loading = true
#action loadPics() {
this.loading = true;
let dataURL = 'some url';
return axios.get(dataURL)
.then(res => {this.picInfo = res.data})
.then(this.loading = false)
}
class PhotoGallery extends React.Component{
componentWillMount(){
this.PhotoStore.loadPics();
}
render(){
console.log(//these two properties)
//returns false and undefined object
return(
//some code
);
}
}
I know I can just check for the picInfo.length before rendering the JSX, but I want to make this work. Thanks for any tips in advance!
You don't need the second .then clause. When you set this.picInfo, also set this.loading. Because you put the loading state change in a chained promise, there is a timing issue where the #observable tries to evaluate before the loading is set.
https://mobx.js.org/best/actions.html - see runInAction and the asyncAction decorator
#action loadPics() {
this.loading = true;
let dataURL = 'some url';
return axios.get(dataURL)
.then(res => {runInAction(() => {this.picInfo = res.data})}
.then(runInAction(() =>this.loading = false))
}

Categories