Get data attribute from html before calling API in React - javascript

I'm building a React component to load blog articles to insert inside a CMS.
I will be getting an author name from a data-name attribute from html.
When I run this code with url2 (hard coded name of the author) everything works. When I run the code with url1 (name of author read from data-attribute) nothing works. Please help.
I have this html:
<div id="dataElement" data-name="Peter Smith"></div>
And this is the code inside my react component:
constructor(props) {
super(props);
this.state = {
insightsData: [],
loading: true,
authorName: document.getElementById('dataElement').getAttribute('data-name').replace(/ /g, "_")
}
}
componentDidMount(){
let self = this;
let url2 = '/api/Insights/GetAuthorArticles?authorName=Peter_Smith&numRecords=5';
let url1 = `/api/Insights/GetAuthorArticles?authorName=${this.state.authorName}&numRecords=5`;
this.setState({loading:true});
var Promise = require("es6-promise");
Promise.polyfill();
axios.get(url)
.then((response) => {
self.setState({
insightsData: response.data.InsightsResults.Records
}, this.setState({loading:false}));
console.log(response.data.InsightsResults.Records);
});
}

The html is loaded only after the constructor. Try this, using react ref:
constructor(props) {
super(props);
this.state = {
insightsData: [],
loading: true,
}
}
componentDidMount(){
let self = this;
let url2 = '/api/Insights/GetAuthorArticles?authorName=Peter_Smith&numRecords=5';
let url1 = `/api/Insights/GetAuthorArticles?authorName=${document.getElementById('dataElement').getAttribute('data-name').replace(/ /g, "_")}&numRecords=5`;
this.setState({loading:true});
var Promise = require("es6-promise");
Promise.polyfill();
axios.get(url)
.then((response) => {
self.setState({
insightsData: response.data.InsightsResults.Records
}, this.setState({loading:false}));
console.log(response.data.InsightsResults.Records);
});
}
EDIT: solution without ref.

<div id="dataElement" data-name="Peter Smith"></div> gets mounted after the constructor. you will never have dataElement in the dom when you run authorName: document.getElementById('dataElement')...
try moving authorName: document.getElementById('dataElement')... to componentDidMount using setState and componentDidMount to componentDidUpdate
Here are the docs, if you wanna deep dive. https://reactjs.org/docs/react-component.html#mounting

Related

Load an array with react.js

I have a problem with the react.js code below, the services performs the loading of an array of values ​​what interests me at this moment is the first value of the array inside the component, if I display the first CodArt value before configuring the setState (so if I view the console.log) the value is printed when I view it through the setState tells me that it is not set.
Services.js :
const axios = require('axios').default;
export async function caricamentoarticoliutilizzati() {
var listaarticoli=[];
await axios.post(server.url+'/articolo/statistiche', {})
.then(function (response) {
let data = response.data;
for (let index in data){
var datatemp={
CodMarca : data[index].CodMarca,
CodArt : data[index].CodArt,
Totale : data[index].Totale
};
listaarticoli.push(datatemp);
}
}).catch(function (error) {
console.log("Errore: "+error);
});
return listaarticoli;
}
Main.js:
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
graph: null,
checkedArr: [false, false, false],
totalePreventiviInseriti: 0,
percentualeAccettati:0,
percentualeRifutati:0,
percentualeInCorso:0,
percentualeCantieriChiusi:0,
percentualeCantieriInCorso:0,
percentualeCantieriContoInviato:0,
percentualeCantieriBolle:0,
articoliUtilizzati:[],
};
this.checkTable = this.checkTable.bind(this);
}
async componentDidMount() {
var totalePreventiviInseriti= await getStatistichePreventivi();
var percentualeAccettati=await getStatistichePreventiviAccettati();
var percentualeRifutati=await getStatistichePreventiviRifiutati();
var percentualeInCorso=await getStatistichePreventiviInCorso();
var cantieriPercentualiChiusi=await getStatisticheCantieri("Chiuso");
var cantieriPercentualiInCorso=await getStatisticheCantieri("InCorso");
var percentualeCantieriContoInviato=await getStatisticheCantieri("Conto Inviato");
var percentualeCantieriBolle=await getStatisticheCantieri("Lavoro terminato inserire bolle ");
var articoliUtilizzati=await caricamentoarticoliutilizzati();
console.log("articoliUtilizzati: "+ articoliUtilizzati[0].CodArt.toString());
//Inserimento valori nello stato
this.setState({ totalePreventiviInseriti: totalePreventiviInseriti });
this.setState({ percentualeAccettati: percentualeAccettati });
this.setState({ percentualeRifutati: percentualeRifutati });
this.setState({percentualeInCorso: percentualeInCorso});
this.setState({percentualeCantieriChiusi: cantieriPercentualiChiusi});
this.setState({percentualeCantieriInCorso: cantieriPercentualiInCorso});
this.setState({percentualeCantieriContoInviato: percentualeCantieriContoInviato});
this.setState({percentualeCantieriBolle: percentualeCantieriBolle});
this.setState({articoliUtilizzati: articoliUtilizzati});
}
render(){
....
<p> this.state.articoliUtilizzati[0].CodArt //-->CodArt not set
}
that is because the call to caricamentoarticoliutilizzati return a promise not the array listaarticoli. so in componenetDidMount the variable articoliUtilizzati holds a promise object you can confirm this by:
var articoliUtilizzati=await caricamentoarticoliutilizzati();
console.log("articoliUtilizzati: "+ articoliUtilizzati.then(arr =>arr[0].CodArt.toString()));

How do I use axios response in different components without using export?

As the tittle says, I would like to be able to use the same axios response for differents components.
I have some restrictions like, I'm onlyl able to use react by adding scripts tags to my html so things like exports or jsx are impossible for me.
This is my react code:
class User extends React.Component {
state = {
user: {}
}
componentWillMount() {
console.log(localStorage.getItem("user"))
axios.get('http://localhost:8080/dashboard?user=' + localStorage.getItem("user"))
.then(res => {
const userResponse = res.data
setTimeout(() =>
this.setState({user: userResponse.user}), 1000);
})
}
render () {
const {user} = this.state
if (user.fullName === undefined)
return React.createElement("div", null, 'loading..');
return React.createElement("span", {className: "mr-2 d-none d-lg-inline text-gray-600 small" }, user.fullName);
}
}
ReactDOM.render( React.createElement(User, {}, null), document.getElementById('userDropdown') );
class Roles extends React.Component{
state = {
user: {}
}
componentWillMount() {
console.log(localStorage.getItem("user"))
axios.get('http://localhost:8080/dashboard?user=' + localStorage.getItem("user"))
.then(res => {
const userResponse = res.data
setTimeout(() =>
this.setState({user: userResponse.user}), 1000);
})
}
render () {
const {user} = this.state
const roles = user.user.roles.map((rol) => rol.roleName)
if (user.fullName === undefined)
return React.createElement("div", null, 'loading..');
return React.createElement("a", {className: "dropdown-item" }, user.fullName);
}
}
ReactDOM.render( React.createElement(Roles, {}, null), document.getElementById('dropdownRol') );
I would like to be able to manage different components(rendering each one) with data of the same axios response.
Is this possible considering my limitations?
Thanks in advance
Here's a working example of how you might do it. I've tried to annotate everything with comments, but I'm happy to try to clarify if you have questions.
// Fake response object for the store's "load" request
const fakeResponse = {
user: {
fullName: "Carolina Ponce",
roles: [
{ roleName: "administrator" },
{ roleName: "editor" },
{ roleName: "moderator" },
{ roleName: "generally awesome person" }
]
}
};
// this class is responsible for loading the data
// and making it available to other components.
// we'll create a singleton for this example, but
// it might make sense to have more than one instance
// for other use cases.
class UserStore {
constructor() {
// kick off the data load upon instantiation
this.load();
}
// statically available singleton instance.
// not accessed outside the UserStore class itself
static instance = new this();
// UserStore.connect creates a higher-order component
// that provides a 'store' prop and automatically updates
// the connected component when the store changes. in this
// example the only change occurs when the data loads, but
// it could be extended for other uses.
static connect = function(Component) {
// get the UserStore instance to pass as a prop
const store = this.instance;
// return a new higher-order component that wraps the connected one.
return class Connected extends React.Component {
// when the store changes just force a re-render of the component
onStoreChange = () => this.forceUpdate();
// listen for store changes on mount
componentWillMount = () => store.listen(this.onStoreChange);
// stop listening for store changes when we unmount
componentWillUnmount = () => store.unlisten(this.onStoreChange);
render() {
// render the connected component with an additional 'store' prop
return React.createElement(Component, { store });
}
};
};
// The following listen, unlisten, and onChange methods would
// normally be achieved by having UserStore extend EventEmitter
// instead of re-inventing it, but I wasn't sure whether EventEmitter
// would be available to you given your build restrictions.
// Adds a listener function to be invoked when the store changes.
// Called by componentWillMount for connected components so they
// get updated when data loads, etc.
// The store just keeps a simple array of listener functions. This
// method creates the array if it doesn't already exist, and
// adds the new function (fn) to the array.
listen = fn => (this.listeners = [...(this.listeners || []), fn]);
// Remove a listener; the inverse of listen.
// Invoked by componentWillUnmount to disconnect from the store and
// stop receiving change notifications. We don't want to attempt to
// update unmounted components.
unlisten = fn => {
// get this.listeners
const { listeners = [] } = this;
// delete the specified function from the array.
// array.splice modifies the original array so we don't
// need to reassign it to this.listeners or anything.
listeners.splice(listeners.indexOf(fn), 1);
};
// Invoke all the listener functions when the store changes.
// (onChange is invoked by the load method below)
onChange = () => (this.listeners || []).forEach(fn => fn());
// do whatever data loading you need to do here, then
// invoke this.onChange to update connected components.
async load() {
// the loading and loaded fields aren't used by the connected
// components in this example. just including them as food
// for thought. components could rely on these explicit fields
// for store status instead of pivoting on the presence of the
// data.user object, which is what the User and Role components
// are doing (below) in this example.
this.loaded = false;
this.loading = true;
try {
// faking the data request. wait two seconds and return our
// hard-coded data from above.
// (Replace this with your network fetch.)
this.data = await new Promise(fulfill =>
setTimeout(() => fulfill(fakeResponse), 2000)
);
// update the loading/loaded status fields
this.loaded = true;
this.loading = false;
// call onChange to trigger component updates.
this.onChange();
} catch (e) {
// If something blows up during the network request,
// make the error available to connected components
// as store.error so they can display an error message
// or a retry button or whatever.
this.error = e;
}
}
}
// With all the loading logic in the store, we can
// use a much simpler function component to render
// the user's name.
// (This component gets connected to the store in the
// React.createElement call below.)
function User({ store }) {
const { data: { user } = {} } = store || {};
return React.createElement(
"span",
{ className: "mr-2 d-none d-lg-inline text-gray-600 small" },
user ? user.fullName : "loading (User)…"
);
}
ReactDOM.render(
// Connect the User component to the store via UserStore.connect(User)
React.createElement(UserStore.connect(User), {}, null),
document.getElementById("userDropdown")
);
// Again, with all the data loading in the store, we can
// use a much simpler functional component to render the
// roles. (You may still need a class if you need it to do
// other stuff, but this is all we need for this example.)
function Roles({ store }) {
// get the info from the store prop
const { data: { user } = {}, loaded, loading, error } = store || {};
// handle store errors
if (error) {
return React.createElement("div", null, "oh noes!");
}
// store not loaded yet?
if (!loaded || loading) {
return React.createElement("div", null, "loading (Roles)…");
}
// if we made it this far, we have user data. do your thing.
const roles = user.roles.map(rol => rol.roleName);
return React.createElement(
"a",
{ className: "dropdown-item" },
roles.join(", ")
);
}
ReactDOM.render(
// connect the Roles component to the store like before
React.createElement(UserStore.connect(Roles), {}, null),
document.getElementById("dropdownRol")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="userDropdown"></div>
<div id="dropdownRol"></div>

Why 'this' is undefined inside a Promise call

I don't understand what's going on
componentDidMount() {
console.log('componentDidMount');
//const self = this;
let _id = this.props.match.params.id.toUpperCase();
if (_id != this.state.id.toUpperCase()) {
axios.get('/data/pricemultifull?fsyms=' + _id + '&tsyms=USD')
.then(response => {
// let _currentcoin = { ...resp.data.RAW.BTC.USD, ticker: _id };
this.setState({ id: _id }); //this == undefined
});
}
}
I can get a response back but this is always undefined and I'm unable to setState. I'm using an arrow function which I thought was scope 'this' to the component level. I can fix it by making a new var and setting 'this' before I make the request. I know that this should be working though. What am I missing?
My entire component
import React, { Component } from 'react';
import axios from '../../axios';
class CoinViewer extends Component {
state = {
coin: {},
hasLoaded: false,
id: ''
}
componentDidMount() {
console.log('componentDidMount');
//const self = this;
let _id = this.props.match.params.id.toUpperCase();
if (_id != this.state.id.toUpperCase()) {
axios.get('/data/pricemultifull?fsyms=' + _id + '&tsyms=USD')
.then( resp => {
// let _currentcoin = { ...resp.data.RAW.BTC.USD, ticker: _id };
this.setState({ id: _id });
});
}
}
componentWillMount() {
}
componentWillUpdate() {
}
componentDidUpdate() {
}
getCompleteCoinData(_id) {
}
render() {
return (
<div>
CoinViewer Component: {this.state.id} sads
</div>
)
}
}
export default CoinViewer
Solution 1: arrow functions..
requestSuccess = (resp) => {
// let _currentcoin = { ...resp.data.RAW.BTC.USD, ticker: _id };
this.setState({ id: _id });
}
componentDidMount() {
console.log('componentDidMount');
//const self = this;
let _id = this.props.match.params.id.toUpperCase();
if (_id != this.state.id.toUpperCase()) {
axios.get('/data/pricemultifull?fsyms=' + _id + '&tsyms=USD')
.then(this.requestSuccess);
}
}
Solution 2: binding
componentDidMount() {
console.log('componentDidMount');
//const self = this;
let _id = this.props.match.params.id.toUpperCase();
if (_id != this.state.id.toUpperCase()) {
axios.get('/data/pricemultifull?fsyms=' + _id + '&tsyms=USD')
.then((resp) => {
// let _currentcoin = { ...resp.data.RAW.BTC.USD, ticker: _id };
this.setState({ id: _id });
}.bind(this));
}
}
:Edit
Wow, the below is kinda true, but the real issue is you didn't initialize state. https://reactjs.org/docs/react-component.html#constructor
constructor() {
super();
this.state = {
coin: {},
hasLoaded: false,
id: ''
}
}
You could use lexical scoping and fix like this, this is a popular pattern to protect this.
Basically, when you use promises or functions from other libraries/ APIs you do not know what they have set their context inside the callback functions to.
In order to use the context you want, you keep the context you need saved in a variable within scope and reference it there _this, rather than by pointing to the context this. I'd recommend reading 'you dont know js' to understand this concept further.
componentDidMount() {
console.log('componentDidMount');
const _this = this;
let _id = _this.props.match.params.id.toUpperCase();
if ( _id != _this.state.id.toUpperCase() ) {
axios.get('/data/pricemultifull?fsyms=' + _id + '&tsyms=USD')
.then(response => {
_this.setState({ id: _id }); //this == undefined
});
}
}
When working with React.js, chances are you have faced a problem how
to access this from inside the promise.There is more than one solution to resolve this reference inside the
promise. The old approach would be setting the self = this
reference While this would work, the recommended solution, which is
more inline with ES6, would be to use an arrow function here:
class Component extends React.Component {
componentDidMount() {
let component = this;
axios.get('http://…').then(function(data) {
component.setState( { name: data.blah } );
});
}
}
The arrow syntax, as stated above, is a much smarter way to allow use
of this to make reference to React.Component classes, as we can see
below:
class Component extends React.Component {
componentDidMount() {
axios.get('http://…').then(data => {
this.setState( { name: data.blah } );
});
}
}
Please note, instead of using
function(data) { //body },
we used data => { //body }, and in this case this reference won’t get the promise instance back.

Cant pass a function to a React child from parent (through a list)

Im trying to pass a simple list from a parent to a set of child components and i cant pass down a function. i have something line for line that is the exact same bloody thing that works but this does not.
Does anyone see what is going wrong?
This is the function in the parent that i cant reach (the console.log is not firing):
setClassImage(name, image){
console.log('inside setClassImage');
var TitleArray = this.state.TitleArray;
TitleArray[0] = name;
var ImageArray = this.state.ImageArray;
ImageArray[0] = image;
this.setState({
characterModal: false,
equipmentModal: false,
TitleArray: TitleArray,
ImageArray: ImageArray
});
}
Setting the list in the parent:
let listClasses;
if(this.state.classResults.length!=0){
listClasses = this.state.classResults.map((item,i) => {
return (
<ListClass key={i} classes={item} setClassImage={this.setClassImage.bind(this)}/>
);
});
}
in the render of the parent:
<SelectorBox>
<ListBox>
{listClasses}
<br/>
</ListBox>
<SelectorButton onClick={(e)=>this.cancelChange(e)}>No Class</SelectorButton>
</SelectorBox>
In the child:
class ListClass extends Component{
constructor(props){
super(props);
this.state={
}
}
getImage(e){
e.preventDefault();
var self = this;
axios.post('http://localhost:5000/post2/characterimage', {
search: this.props.classes,
})
.then((response)=>{
console.log('response from the pixabay call ', response.data);
self.props.setClassImage(self.props.classes, response.data);
})
.catch(()=>{
console.log('hello axios error');
});
}
render(){
return(
<div>
<ItemContainer onClick={(e)=>this.getImage(e)}>
<p>
{this.props.classes}
</p>
</ItemContainer>
</div>
)
}
}
I can see console.log('response from the pixabay call ', response.data); and it fires correctly from data on the backend. But the function is not called! The weirdly named containers are purely for styling, so just assume they all act like divs.
The arrow function in your .then() call will automatically bind this to the proper context, so you don't need to do the var self = this; However you do need to bind your getImage function in your constructor. OR
Update your function definition to:
....
getImage = (e) => {
e.preventDefault();
axios.post('http://localhost:5000/post2/characterimage', {
search: this.props.classes,
})
.then((response) => {
console.log('response from the pixabay call ', response.data);
this.props.setClassImage(this.props.classes, response.data);
})
.catch(()=>{
console.log('hello axios error');
});
}
....
}
Otherwise, bind getImage in your constructor:
constructor(props){
super(props);
this.state = {};
this.getImage = this.getImage.bind(this);
}

Getting Object: "undefined" on React

I am a newbie in react JS and I am trying to pull data from a url in Json format.
I did the following but I keep on getting a feeback at the console as
Rovers: undefined.
How do I go about it when am supposed to get something like
Rovers:[object, object, object]
class App extends React.Component {
constructor(props){
super(props);
this.state={rovers:[]};
}
componentWillMount(){
api.getRovers().then((response) =>{
this.setState({
rovers: response.rovers
});
});
}
render() {
console.log("Rovers: ", this.state.rovers);
}
and this is where am calling the json link
var api={
getRovers(){
var url='https://jsonplaceholder.typicode.com/posts/1';
return fetch(url).then((response)=> response.json());
}
};
module.exports=api;
The endpoint replies with object that does not include rovers. However, it includes : id, userId, title and body
That's why response.rovers is undefined. Then this.state.rovers is the same
So , you might mean body instead of rovers , in this case , replace:
componentWillMount(){
api.getRovers().then((response) =>{
this.setState({
rovers: response.rovers
});
});
}
By :
componentWillMount(){
api.getRovers().then((response) =>{
this.setState({
rovers: response.body.split('\n')
});
});
}

Categories