Working with older React.
How to correctly get data from an endpoint?
It complains about state
I am a React newbie. Looked on Tutorials but it seems developer who did this code used some other convention in creating Classes/Components.
Tried this:
import React from "react"
import Utils from "utils"
import {A, makeUrl, redirectTo} from "routing"
import Settings from "settings"
import ProjectApi from "api/project"
import FlashMessagesService from "flash_messages"
var ProjectHeader = React.createClass({
displayName: 'ProjectHeader',
state = {
project: [],
},
componentDidMount() {
// need to make the initial call to getData() to populate
// data right away
// Now we need to make it run at a specified interval
setInterval(this.refresh, 1000); // runs every 5 seconds.
},
refresh : function(){
var props = this.props
var providers = Settings.providers['project.header.info'] || []
providers
.filter(function(provider) {
return provider.component.isApplicable(props.project)
})
.forEach(function (provider) {
projectInfo.push(<provider.component project={props.project} {...props}/>)
})
fetch('/api/v1/project/'+props.project.pk)
.then(response => response.json())
.then(data => {
this.setState({ project: data.project });
});
if (this.state.project.analysis_status == 'succeeded') {
window.location.reload();
}
},
Thanks,
There was a typo in the state line (line 11) which should have been state: { rather than state = {. Another potential complaint it might have had is that during runtime after the component was unmounted, the interval would've continued firing, so I added a clearInterval in componentWillUnmount.
import Utils from 'utils';
import { A, makeUrl, redirectTo } from 'routing';
import Settings from 'settings';
import ProjectApi from 'api/project';
import FlashMessagesService from 'flash_messages';
var ProjectHeader = React.createClass({
displayName: 'ProjectHeader',
intervalId: null,
state: {
project: [],
},
componentDidMount() {
// need to make the initial call to getData() to populate
// data right away
// Now we need to make it run at a specified interval
this.intervalId = setInterval(this.refresh, 1000); // runs every 5 seconds.
},
componentWillUnmount(){
clearInterval(this.intervalId);
},
refresh: function () {
var props = this.props;
var providers = Settings.providers['project.header.info'] || [];
providers
.filter(function (provider) {
return provider.component.isApplicable(props.project);
})
.forEach(function (provider) {
projectInfo.push(
<provider.component project={props.project} {...props} />
);
});
fetch('/api/v1/project/' + props.project.pk)
.then((response) => response.json())
.then((data) => {
this.setState({ project: data.project });
});
console.log(hits);
if (hits.analysis_status == 'succeeded') {
window.location.reload();
}
},
Related
I am creating an app with TypeScript + Firebase. I've followed this website to set it up: https://rnfirebase.io. After I finished with authentication I wanted to get a value from the real time database. However making the request doesn't resolve. I've also put it in the await version however that didn't resolve either.
import React, { useEffect } from "react";
import { Text } from "react-native";
import { firebase } from "#react-native-firebase/database";
import { REALTIME_DATABASE_ENV } from "react-native-dotenv";
const TestPage = () => {
useEffect(() => {
const reference = firebase
.app()
.database(REALTIME_DATABASE_ENV)
.ref("particularities/")
.once("value")
.then((snapshot) => {
console.log(`snapshot: ${snapshot.val()}`);
//expected result:
// {
// sickness: {
// label: "Sickness",
// },
// allergic: {
// label: "Allergic",
// },
// };
})
.catch((e: unknown) => {
console.log(`catch: ${e}`);
});
}, []);
return (
<Text>Test page</Text>
);
};
export default TestPage;
The rules that are applied to the real time database:
{
"rules": {
".read": false,
".write": false,
// ...
"particularities": {
".read": true,
".write": true,
},
}
}
Thing we found: logging out of the app does resolve all the requests made. Testing while logged in and all rules set to public gives the same result as before with the promise not resolving
As per the documentation here is how you can read the data once.
https://rnfirebase.io/database/usage#one-time-read
You don't need to pass database ref other than an 'us-central1'
import database from '#react-native-firebase/database';
database()
.ref('particularities/')
.once('value')
.then(snapshot => {
console.log('Data: ', snapshot.val());
})
.catch((e: unknown) => {
console.log(`catch: ${e}`);
});
So I am trying to build a cart using react and express. The backend is working fine. I am using postman to test my endpoint and it is giving me the correct response. However, it is the react frontend that is causing problems.
I am trying to use map function on the items array which has been set to the response from the server, but it gives me an error:
TypeError: Cannot read property 'map' of undefined
Here is my code:
Cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data });
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
CartItem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props =>
props.items.map(item => {
return <div>{item.title}</div>;
});
export default CartItem;
And the postman response for '/cart'
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
}
],
"total": 25.65
}
here is also the server.js code I dont understand why my array is empty when the postman is giving me a response, that indicates my endpoints work correctly.
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
I have already spent a day and a half trying to resolve this on my own. Any help would be immensely appreciated.
As another answer is pointing your initialState is incorrect as you are accessing in the checkItems method, you have to preserve the structure.
I could suggest to mantain certain stucture, in your case looks like this is your initialState :
this.state = {
items: []
};
So when you are calling you method checkItems you are accessing it the wrong way:
checkItems() {
return this.state.items.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
So as far as i can see when you receive your response you are modifying your initialState structure.
To fix this i suggest you this minor change (just change the response.data to response.data.items):
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data.items });
})
.catch(function(err) {
console.log(err);
});
}
And the method checkItems as well:
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
This happens because when you are loading the app, your initial state is wrong. this.state.items is an array, but this.state.items.items is undefined, and for the .map() function to work you need an array.
So your initial state should look something like this:
this.state = {
items: {
items: []
}
};
It seems like the structures of your initial data and data from the api do not match. You need to change the checkItems method and also set the nested items to the state:
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({ items: response.data.items }); // set items to the state
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => { // items is one level deep now
return <CartItem book={currItem} key={i}></CartItem>;
});
}
The reason for the error is that your initial state is items: [], which is what being used for the first render. So basically in checkItems you're trying to access items property of an empty array.
Edit: You're also accessing incorrect props in the child component, it'd be book, not items, since it's what you're passing:
const CartItem = ({book}) => {
return <div>{book.title}</div>
}
I am currently able to get user data from the Firestore however I'm having trouble saving the users document data. I'm getting an error below in my console
TypeError: this.setState is not a function
at Object.next (RepRequest.js:32)
at index.cjs.js:1344
at index.cjs.js:1464
I attempted to follow another user's question from
Can't setState Firestore data, however still no success.
I do have a two api request right after getting the data and I am able to setState then. I tried incorporating the Firestore request in the promise.all but was unable to successfully, which is why I have it separated. Maybe I'm headed down the wrong path, any guidance is appreciated.
import React, { useEffect, useState } from "react";
import app from "./config/base.js";
import axios from "axios";
export default class RepRequest extends React.Component {
constructor(props) {
super(props);
this.state = {
userInfo: [],
fedSens: [],
fedReps: []
};
}
componentDidMount() {
const items = [];
app.auth().onAuthStateChanged(function(user) {
if (user) {
console.log("User is signed in");
let db = app
.firestore()
.collection("user")
.doc(user.uid);
db.get().then(doc => {
if (doc.exists) {
console.log("Document data:", doc.data());
items.push(doc.data());
} else {
console.log("No doc exists");
}
});
}
this.setState({ userInfo: items });
});
Promise.all([
axios.get(
`https://api.propublica.org/congress/v1/116/senate/members.json`,
{
headers: { "X-API-Key": "9wGKmWl3kNiiSqesJf74uGl0PtStbcP2mEzSvjxv" }
}
),
axios.get(
`https://api.propublica.org/congress/v1/116/house/members.json`,
{
headers: { "X-API-Key": "9wGKmWl3kNiiSqesJf74uGl0PtStbcP2mEzSvjxv" }
}
)
]).then(([rest1, rest2]) => {
this.setState({
fedSens: rest1,
fedReps: rest2
});
});
}
render() {
if (this.state.fedReps.length <= 0)
return (
<div>
<span>Loading...</span>
</div>
);
else {
console.log(this.state.fedReps);
return <div>test</div>;
}
}
}
Your problem arises from mixing lambda function declarations ((...) => { ... }) and traditional function declarations (function (...) { }).
A lambda function will inherit this from where it was defined but a traditional function's this will be isolated from the context of where it was defined. This is why it is common to see var self = this; in legacy-compatible code because this usually didn't match what you wanted it to.
Here is an example snippet demonstrating this behaviour:
function doSomething() {
var anon = function () {
console.log(this); // 'this' is independent of doSomething()
}
var lambda = () => {
console.log(this); // inherits doSomething's 'this'
}
lambda(); // logs the string "hello"
anon(); // logs the 'window' object
}
doSomething.call('hello')
Solution
So you have two approaches available. Use whichever you are comfortable with.
Option 1: Use a lambda expression
app.auth().onAuthStateChanged(function(user) {
to
app.auth().onAuthStateChanged((user) => {
Option 2: Assign a "self" variable
const items = [];
app.auth().onAuthStateChanged(function(user) {
// ...
this.setState({ userInfo: items });
}
to
const items = [];
const component = this; // using "component" makes more sense than "self" in this context
app.auth().onAuthStateChanged(function(user) {
// ...
component.setState({ userInfo: items });
}
I am using the latest version of ReactJS and making requests with Axios. But before I entered the cancel() function I was getting the following error:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in MenuPlaylist (at Sidebar/index.js:19)
in aside (created by Context.Consumer)
in StyledComponent (created by styled.aside)
in styled.aside (at Sidebar/index.js:11)
in Sidebar (at Search/index.js:16)
in Search (created by Context.Consumer)
Because the problem was as soon as the component disassembled it leaked the memory. But now canceling the request I get the following messages on the console:
And I find it quite strange, as it seems that the system is in trouble.
What is the correct way to cancel requests to avoid memory leaks?
COMPONENT:
import React, { Component } from "react";
// STYLES
import { Menu, Title } from "./styles";
// SERVICES
import { cancelAxiosRequest, getAllPlaylist } from "services/Api";
// SUBCOMPONENT'S
import { CreatePlaylist, CreatedPlaylist } from "components";
class MenuPlaylist extends Component {
state = {
data: []
};
// LIFE CYCLES
componentDidMount() {
this.consumeAPI();
}
componentWillUnmount() {
cancelAxiosRequest("Request Canceled.");
}
// METHODS
consumeAPI = () => {
getAllPlaylist().then(({ data }) => {
this.setState({ data: data });
});
};
render = () => {
return (
<Menu>
<Title>PLAYLISTS</Title>
<CreatePlaylist />
<CreatedPlaylist data={this.state.data} />
</Menu>
);
};
}
export default MenuPlaylist;
AXIOS:
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:3001",
timeout: 1000
});
let CancelToken = axios.CancelToken;
export let cancelAxiosRequest;
// GET'S
export function getNewReleases() {
return instance.get("/newReleases");
}
export function getAllPlaylist() {
return instance.get("/playlist", {
cancelToken: new CancelToken(function executor(c) {
cancelAxiosRequest = c;
})
});
}
export function getPlaylist(name) {
return instance.get("/playlist", {
params: {
name: name
}
});
}
// POST'S
export function postNewPlaylist(id, name) {
return instance.post("/playlist", {
id: id,
to: `/playlist/${id}`,
name: name
});
}
According to the documentation "cancelling" means the promise throws a Cancel error with the message you specify.
to fix:
// ...
getAllPlaylist()
.then(({ data }) => {
this.setState({ data });
})
.catch(err => {
if (axios.isCancel(err)) {
return; // ignore
}
throw err; // or handle other errors
});
// ...
Though I feel changing the value of an export is not really the best way to use cancel tokens. Check if you need to be unmounting the component in the first place, but if it truly is just going to be unmounted sometimes, maybe try something like
export function getAllPlaylist(cancelToken) {
return instance.get("/playlist", {
cancelToken: cancelToken
});
}
// ...
getAllPlaylist(new CancelToken(c => this.cancel = c))
.then(({ data }) => {
this.setState({ data });
});
// ...
componentWillUnmount() {
this.cancel();
}
I don't know if this is 100% ideal, but this avoids multiple instances of the component fighting over a single variable.
in the catch you should do something like
.catch((e) => {
if (axios.isCancel(e)) return;
setError(true);
});
because canceling isn't an error
I am trying to create a simple real time chat app to practise with socket-io. I am using React to create UI and i am using NodeJS with Express web server. I have ChatPage React component to show messages.
I am emitting an event newMessage when a user joined to a room and i am sending a welcome message as data from server to client. At that time i am routing to ChatPage component and i am subscribing to updateUserList event in componentWillMount but this event is not coming up to that callback i defined. After some trials i realized a weird thing about this event. I subscribed this event from another .js file and i could see data that i sent from server.
// server.js
socket.on('joinRoom', (data, callback) => {
const { username, roomName } = data
if (username === '' || roomName === '') {
if (callback) {
return callback({ error: 'Username and password required!' })
}
return
}
socket.join(roomName, (err) => {
if (err && callback) {
return callback({ error: 'Could not connect to room.' })
}
socket.emit('newMessage', generateMessage('Admin', 'Welcome to chat app'))
if (callback) {
callback()
}
})
})
// ChatPage.js
componentWillMount() {
const socket = io('http://localhost:3400')
socket.on('newMessage', (message) => {
console.log('newMessage', message)
})
}
// event is coming to here instead!
import socketIOClient from "socket.io-client";
export const getSocketIOClient = () => {
const endpoint = "http://localhost:3400"
const socket = socketIOClient(endpoint)
socket.on('newMessage', (er) => {
console.log('newMessage', message)
})
return socket;
}
I faced similar porblem in my project. There I noticed few things which are missing in your code.
You need to call back join room in client code as well in componentWillMount() method.
You need to initialize socket in state variable. I don't know exactly why it doesn't work when we initialize in const variable but it works when we use state variable instead. I verified this code, it works fine even in the first time react component mount.
Here, is sample code:
import React from 'react'
import io from 'socket.io-client';
class SocketExample extends React.Component {
state = {
chatMessage: '',
chatMessages: [],
socket: io.connect(`http://localhost:3001`),
}
submitChatMessage = (e) => {
e.preventDefault()
if (this.state.chatMessage.length > 0) {
this.state.socket.emit('newMessage', {
username: this.props.currentUser.username,
message: this.state.chatMessage,
});
this.setState({
chatMessage: '',
})
}
}
componentDidMount(){
this.state.socket.on('connect', () => {
this.state.socket.emit('join', { uniqueValue: chatRoomName });
});
this.state.socket.on("newMessage", data => {
this.setState({
chatMessages: [...this.state.chatMessages, data],
});
});
}
render() {
return (
<div>{this.state.chatMessages.map(chat => {
return <p>{chat.username + ': ' + chat.message}</p>
})}</div>
)
}
}
export default SocketExample