Accessing data returned from promise in Redux - arrays

I'm trying to access the data of the first dataElement in the array. How can I reach it? I want to console.log it's name.
import React, { Component } from 'react';
class Submit extends Component {
componentDidMount() {
const programStage = this.props.getProgramStage();
if (programStage !== null) {
console.log('Stage loaded...');
}
console.log(this.props.getForm());
}
render() {
return <div />;
}
}
export default Submit;
How the console looks like

As shown in the pic, the promise is resolved. Hence you should be able to access the data like :
this.props.getForm().then((data) => console.log(data[0].name))

It seems that the return type of the call getForm() is a Promise (according to the output). You would need to append a handler via the then method of the promise to actually get the value you are looking for.
E.g.
componentDidMount() {
...
this.props.getForm().then(result => console.log(result))
}

Related

How to retrieve user data from fire store

I need to fetch the user data and display it. I am getting an error now that says
TypeError: this.unsubscribe is not a function
and when I initialise it as a normal variable like const db, then I get another error
Function CollectionReference.doc() requires its first argument to be of type non-empty string
import React from "react";
import { auth, firestore } from "../../firebase/firebase.utils";
export default class UserPage extends React.Component {
state = {
user: {}
};
unsubscribe = null;
componentDidMount() {
const user = auth.currentUser;
this.unsubscribe = firestore
.collection("users")
.doc(user)
.onSnapshot(doc => {
this.setState = {
user: doc.data()
};
});
}
componentWillMount() {
this.unsubscribe();
}
render() {
return (
<div>
<h1>{this.state.user.name}</h1>
</div>
);
}
}
when I insinalise it as a normal variable like const db
Not quite sure what you mean by this, but if you're getting an error about the type of unsubscribe, I suggest using console.log right before you call it to view its value.
Bear in mind that componentWillMount happens in the lifecycle before componentDidMount (hence the names will and did). I suspect that's one of your problems: you try to call unsubscribe before setting the value.
With regard to your other error about the doc call, it's likely referring to:
...
.collection("users")
.doc(user) <-- this line
.onSnapshot(doc => {
this.setState = {
us
...
As the error output states, that user variable (the first argument of doc) must be a string, and it can't be an empty string.
I don't see user anywhere in your code, so I expect that it's currently the value undefined. You could access this.state.user here, but I'd strongly advise against it since you subsequently set that state in the call (probably cause an infinite loop).
What is your end goal? What have you tried to resolve these two issues? Maybe adding that to your question would help us assist you better.

Redux-saga: Best way to handle async call data used in only one component

I need advice concerning redux-saga and the way to handle async call. I don't find anwsers to my questions.
I would like to know how can I handle properly async API call which return data used in only one component (so useless to store it in the store) ?
In my react application, I use redux-saga to handle async call. When the saga finish correctly, I dispatch a success action which store result in the store.
However, i find useless to store the result when I only want to display it in one component. Instead I would like to run a saga and return by a callback data to my component without storing it int the store. Is it possible ? How can I do that ?
thanks.
Here is a sample code for you, that code makes an api request in componentDidMount lifecycle and sets the data to its state and after it renders it.
import React, { Component } from "react";
import { render } from "react-dom";
import axios from 'axios';
class App extends Component {
constructor() {
super();
this.state = {
data: []
};
}
async componentDidMount() {
try {
let response = await axios.get('https://jsonplaceholder.typicode.com/users');
console.log('response.data: ', response.data);
this.setState({
data: response.data
});
} catch (error) {
console.log('error: ', error);
}
}
render() {
return (
<ul>
{this.state.data.map(item => <li>{item.name}</li>)}
</ul>
);
}
}
Hope this helps.

React Meteor How to execute a function after withTracker data arrives?

I am publishing data from server and catching it using withTracker.
export default withTracker(() => {
let towndatasub = Meteor.subscribe("userTownDataPublisher",Meteor.userId());
let resourcedatasub = Meteor.subscribe("userResourcePublisher",Meteor.userId());
return{
townData : Towns.find({"ownerId":Meteor.userId()}).fetch(),
resourceData : Resources.find({"ownerId":Meteor.userId()}).fetch()
}
})(TownPage);
The problem is i would like to run a function when townData and resourceData arrives.If i call updateResources in componentDidMount i get undefined on this.props.componentWillReceive props not called.townData and this.props.resourceData
updateResources = () =>{
Meteor.call("updateUserResources",Meteor.userId(),(err,result)=>{
if(err){
console.log(err)
}else{
console.log("asdasd");
//console.log(this.props.resourceData); undefined
// here i will do something with this.props.resourceData
}
})
}
So where should i call updateResources function to not get undefined ?
Firstly, componentDidMount is only called once when a page is loaded, right after the first call to render finishes. Therefore, you shouldn't call updateResources there since there's a chance that the collections haven't finished loading from the server by then. I would recommend calling it in render because render will be called once before the data has arrived and again after the data arrives.
Secondly, if you want to be even more accurate with when the data arrives, you can return two more properties in withTracker involving the ready function like so,
export default withTracker(() => {
let towndatasub = Meteor.subscribe("userTownDataPublisher",Meteor.userId());
let resourcedatasub = Meteor.subscribe("userResourcePublisher",Meteor.userId());
return{
townData : Towns.find({"ownerId":Meteor.userId()}).fetch(),
resourceData : Resources.find({"ownerId":Meteor.userId()}).fetch(),
townsReady : towndatasub.ready(),
resourcesReady : resourcedatasub.ready()
}
})(TownPage);
And then in render, you can call updateResources only when the data has arrived,
if(this.props.townsReady && this.props.resourcesReady) {
this.updateResources();
}

Accessing a promise with the componentDidMount

I'm accessing a promise that gets returned from my mock API. The React component looks like what you see below
import React from 'react';
import StudentListStatistics from './StudentListStatistics';
import StudentStatisticsApi from '../../api/mockStudentApi';
class AboutPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
studentsStatistics: []
};
}
componentDidMount() {
StudentStatisticsApi.getAllStudentsStatistics().then(
studentsStatistics => {
this.setState({
studentsStatistics: studentsStatistics
});
debugger;
}
);
console.log(this.state.studentsStatistics);
}
render() {
return (
<div>
<h2>Student Body Statistics</h2>
<StudentListStatistics studentsStatistics={this.state.studentsStatistics}/>
</div>
);
}
the mock API looks like this
class StudentApi {
static getAllStudentsStatistics() {
return new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve(Object.assign([], studentsStatistics));
}, 1000);
});
}
I'm not sure why the this.state.studentsStatistics is always an empty array. If I step through the code then studentsStatistics array is being returned correctly from my mock API within the then callback.
Can someone point out what I might be missing.
The issue is two-fold:
getAllStudentsStatistics() is asynchronous, which means that it will eventually yield a result, but not immediately;
setState() is also "asynchronous", in that it won't change this.state immediately after it got called.
To work around that, and log the mocked data, you need to first wait for the promise to resolve, and then to also wait for setState to acknowledge that the state has changed (by passing it a callback function):
componentDidMount() {
let promise = StudentStatisticsApi.getAllStudentsStatistics();
promise.then(studentsStatistics => {
this.setState({
studentsStatistics: studentsStatistics
}, () => {
console.log(this.state.studentsStatistics);
}
});
}
I think this also means that your StudentListStatistics component will initially be rendered with an empty array as input. Only once the promise has been resolved will it receive the mocked data.

Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch

I am using ALT for my ReactJS project. I am getting the cannot 'dispatch' error if the ajax call is not yet done and I switch to another page.
Mostly, this is how my project is setup. I have action, store and component. I querying on the server on the componentDidMount lifecycle.
Action:
import alt from '../altInstance'
import request from 'superagent'
import config from '../config'
import Session from '../services/Session'
class EventActions {
findNear(where) {
if (!Session.isLoggedIn()) return
let user = Session.currentUser();
request
.get(config.api.baseURL + config.api.eventPath)
.query(where)
.set('Authorization', 'Token token=' + user.auth_token)
.end((err, res) => {
if (res.body.success) {
this.dispatch(res.body.data.events)
}
});
}
}
export default alt.createActions(EventActions)
Store
import alt from '../altInstance'
import EventActions from '../actions/EventActions'
class EventStore {
constructor() {
this.events = {};
this.rsvp = {};
this.bindListeners({
findNear: EventActions.findNear
});
}
findNear(events) {
this.events = events
}
}
export default alt.createStore(EventStore, 'EventStore')
Component
import React from 'react';
import EventActions from '../../actions/EventActions';
import EventStore from '../../stores/EventStore';
import EventTable from './tables/EventTable'
export default class EventsPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
events: [],
page: 1,
per: 50
}
}
componentDidMount() {
EventStore.listen(this._onChange.bind(this));
EventActions.findNear({page: this.state.page, per: this.state.per});
}
componentWillUnmount() {
EventStore.unlisten(this._onChange);
}
_onChange(state) {
if (state.events) {
this.state.loading = false;
this.setState(state);
}
}
render() {
if (this.state.loading) {
return <div className="progress">
<div className="indeterminate"></div>
</div>
} else {
return <div className="row">
<div className="col m12">
<h3 className="section-title">Events</h3>
<UserEventTable events={this.state.events}/>
</div>
</div>
}
}
}
componentDidMount() {
EventStore.listen(this._onChange.bind(this));
EventActions.findNear({page: this.state.page, per: this.state.per});
}
This would be my will guess. You are binding onChange which will trigger setState in _onChange, and also an action will be fired from findNear (due to dispatch). So there might be a moment where both are updating at the same moment.
First of all, findNear in my opinion should be as first in componentDidMount.
And also try to seperate it in 2 differnet views (dumb and logic one, where first would display data only, while the other one would do a fetching for example). Also good idea is also to use AltContainer to actually avoid _onChange action which is pretty useless due to the fact that AltContainer has similar stuff "inside".
constructor() {
this.events = {};
this.rsvp = {};
this.bindListeners({
findNear: EventActions.findNear
});
}
findNear(events) {
this.events = events
}
Also I would refactor this one in
constructor() {
this.events = {};
this.rsvp = {};
}
onFindNear(events) {
this.events = events
}
Alt has pretty nice stuff like auto resolvers that will look for the action name + on, so if you have action called findNear, it would search for onFindNear.
I can't quite see why you'd be getting that error because the code you've provided only shows a single action.
My guess however would be that your component has been mounted as a result of some other action in your system. If so, the error would then be caused by the action being triggered in componentDidMount.
Maybe try using Alt's action.defer:
componentDidMount() {
EventStore.listen(this._onChange.bind(this));
EventActions.findNear.defer({page: this.state.page, per: this.state.per});
}
I believe it's because you're calling an action, and the dispatch for that action only occurs when after the request is complete.
I would suggest splitting the findNear action into three actions, findNear, findNearSuccess and findNearFail.
When the component calls findNear, it should dispatch immediately, before even submitting the reuqest so that the relevant components will be updated that a request in progress (e.g. display a loading sign if you like)
and inside the same action, it should call the other action findNearSuccess.
The 'Fetching Data' article should be particularly helpful.

Resources