I am using react to fetch a response from a server. I'm essentially scraping data from a chart to make a better chart. Anyway, the response comes back as javascript variables
'var x = [["data","like"this"],["and","like","this"]]'
When I use a plain .js file I can eval the response and then return x and it works.
However that same code when running inside of a React Class doesn't work.
Anyone know why or if somehow that variable is attaching to a different scope? I'm under the impression react doesn't run in strict mode so I think eval should work as far as setting variables.
import React from "react";
import ReactDom from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.getData();
}
getData() {
fetch("url-to-scrape")
.then(function(response) {
return response.text();//only way I can view the response
})
.then(data => {
eval(data) //should set variable x
console.log(x)// this is where I will setState
});
}
render() {
return (
<div>
<p>Smarter Chart</p>
</div>
);
}
}
ReactDom.render(<App />, document.getElementById("app"));
I have no clue why my response is coming back as Javascript variables. Either way, I figured out the way to parse this data in React.
import React from "react";
import ReactDom from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
fetch("url-to-scrape")
.then(response => { //ReadableStream hiding response
return response.text(); //Convert ReadableStream to text
})
.then(data => {
var data_from_x = eval(data + "; x;"); //eval x and return it
this.setState({data: data_from_x });
})
}
render() {
return (
<div>
<p>welcome to the app</p>
</div>
);
}
}
ReactDom.render(<App />, document.getElementById("app"));
I eval the response text which is a string 'var x = data' In the eval I then add + '; x;' which calls that variables data within the eval. I set that data to a variable and then return it or in my case, setState. I now have a good amount of data for a fully customized chart.
I situations where I get multiple variables I can just pull all of them out the same way by name.
Since you expect to use x, there seems to be a guaranteed structure to the data. Assuming this to be a single variable declaration, you can extract this into json by grabbing a substring immediately after the equal sign. While this code needs to be changed if there is more than one variable in the response it is both safe to use and void of scope issues in contrast to eval.
const response = 'var x = [["data","like", "this"],["and","like","this"]]'
const output = JSON.parse(response.substring(response.indexOf('=') + 1))
console.log(output)
Related
I'm currently working on using React to upload a CSV file and convert the data to an array so I can access phone numbers. I've actually got it almost completely functional, with just one problem: I can't figure out how to store the array properly in a variable (dataDump) on the global level. It stores it inside another array.
Here's a picture of my console so you can see what I mean.
I'm able to access the contents of dataDump if I use dataDump[0] (as seen in the function for handleClick), but that won't work for a global variable. I need to be able to send the array's values to other components/files, so I don't think having to call it like that will work. Chances are I'm over-complicating this in my head and the answer is incredibly simple, but I've spent the past 2-3 weeks learning React, Twilio, Mongodb etc. from scratch so my brain's not cooperating.
I'll appreciate any help! Thanks! Code below. (Note this is a component that's imported to the App page.)
import React from "react";
import CSVReader from "react-csv-reader";
var dataDump = [];
console.log(dataDump);
const papaparseOptions = {
header: true,
dynamicTyping: true,
skipEmptyLines: true,
transformHeader: header => header.toLowerCase().replace(/\W/g, "_"),
complete: function(results) {
dataDump.push(results.data);
console.log(dataDump);
var rows = results.data;
let numbers = rows.map(a => a.phone_number); //make the results ONLY the phone numbers
// console.log(numbers);
document.getElementById("data2").innerHTML=numbers; //display the phone numbers
}
};
class Import extends React.Component {
constructor(props) {
super(props);
this.state = {data:[]};
this.handleClick = this.handleClick.bind(this);
}
handleForce = data => {
// console.log(data.length);
console.log(data);
this.setState({data: data});
};
handleClick = () => {
console.log("success");
console.log(this.state.data);
console.log("Next is Numbies:");
let numbies = dataDump[0].map(a => a.phone_number);
console.log(numbies);
document.getElementById("data").innerHTML=numbies;
}
render() {
return (
<div className="container">
<CSVReader
className="csv-input"
label="Select CSV file to import"
onFileLoaded={this.handleForce}
parserOptions={papaparseOptions}
/>
<div>
</div>
<button onClick={this.handleClick.bind(this)}>
Test
</button>
<div id="data" />
<div id="data2" />
<div id="data3">
</div>
</div>
);
}
}
export default Import;
// export default DataController;
Under the hood React-Redux is using context and hooks these days, so don't bother implementing a Redux stack until you've outgrown the simpler, React API, or at least you've fixed your issue. Folks joke that Redux is like shooting a fly with a bazooka. More info on React-Redux internals here and here's the documentation for React's Context.
Some psuedo-code to get you on the right path:
// context.js
import { createContext } from 'react';
export const Store = createContext();
// app.js
import React from 'react';
import { Store } from './context';
import Import from './import'; // I wouldn't change the casing on or reuse a reserved keyword personally, maybe calling this something like 'CsvImporter' would be an improvement
function App() {
const [dataDump, setDataDump] = React.useState([]);
return (
<Store.Provider value={{ dataDump, setDataDump }}>
<Import dataDump={dataDump} setDataDump={setDataDump} />
</Store.Provider>
);
}
Now your import component has two new props, dataDump and setDataDump. You can call setDataDump just like any other call to setting state. Nice!
So you need the dataDump in a new component? That's easy peasy, lemon squeezy, and all without global variables or tossing module scoping to the side:
// foobar.js
import React from 'react';
import { Store } from './context';
export function Foobar() {
// you probably want to do more than force render an array as a string, but this is just a proof of concept
return (
<Store.Consumer>
{({ dataDump, setDataDump }) => (
<p>
`${dataDump}`
</p>
)}
</Store.Consumer>
);
}
Just make sure that Foobar or other components are rendered as children of the Provider in app.js and now you have a 'global' context for passing around dataDumps.
I'm new on react and have a simple application as this:
My purpose for this app it's to consume an Spring Boot REST Service and print the data json on my react app.
I achieve this following this tutorial: https://github.com/marmelab/restful.js/tree/master
But now I'm stuck on a simple problem, don't know how to print the values on a html tag, this is an example of my code:
import React from 'react';
import request from 'request';
import restful, { requestBackend } from 'restful.js';
const api = restful('http://someUrl/v1/mobile', requestBackend(request));
const totals = api.one('statusOrders',1); //organizationID = 1
totals.get().then((response) => {
const requestBody = response.body();
const totalsOrders = requestBody.data(); /*Need to print this on the <div>**/
})
class RestGps extends React.Component {
render(){
return(
<div className="container">
<p>Hello World</p>
//Here I want to print the values.
</div>
)
}
}
export default RestGps
The const totalsOrders has the values of the request, the request structure it's like this:
{
"remissionOk": 109,
"remissionAlert": 5,
"remissionError": 17,
"remissionOutOfTime": 82
}
¿Can someone please tell me how can I print this totalsOrders on my html as my text "Hello World"? Regards.
First you need to change some things around.
Your totalOrders object needs to be within the scope of your RestGps class.
You aren't using states, which can cause a lot of weird behaviour!
I suggest doing the following:
import React from 'react';
import request from 'request';
import restful, { requestBackend } from 'restful.js';
const api = restful('http://someUrl/v1/mobile', requestBackend(request));
const totals = api.one('statusOrders',1); //organizationID = 1
class RestGps extends React.Component {
state = { text: "loading ..." };
componentDidMount = () => {
totals.get().then((response) => {
const requestBody = response.body();
const totalsOrders = requestBody.data(); // assuming this is object
this.setState({ text: JSON.stringify(totalsOrders) });
})
}
render = () => {
return(
<div className="container">
<p>{this.state.text}</p>
//Here I want to print the values.
</div>
)
}
}
export default RestGps
Why are using states?
Well. Initially, your component won't have any data to show. This is because get takes time to fetch remote resources.
So what will tell react to re-render your component, to show the text once the resource gets loaded? Nothing! This is why you need to use states.
What is componentDidMount?
componentDidMount is a function part of the react lifecycle. It is called when the component initially gets rendered. When it renders, you want to fetch the resource, then update our state.
How do we pass the string into the paragraph element?
This is simple, you can just reference your state in the render function, using this.state.text, then add it as a text node in <p>
You can make use of life cycle methods.
class RestGps extends React.Component {
state = {
totalsOrders : null,
};
componentDidMount() {
const api = restful('http://someUrl/v1/mobile', requestBackend(request));
const totals = api.one('statusOrders',1); //organizationID = 1
totals.get().then((response) => {
const requestBody = response.body();
const totalsOrders = requestBody.data(); /*Need to print this on the <div>**/
this.setState({totalsOrders: totalsOrders });
})
}
render(){
const {totalsOrders} = this.state;
return(
<div className="container">
<p>Hello World</p>
totalsOrders.map(item => {
{item}
});
</div>
)
}
}
Notes: using React for this.
Basically, I'm just trying to make a list of anchor elements from a list of links I have stored locally in a json file. I can confirm that the file is successfully seeing the "endpoints" data through console logs. However, the page just renders a white page and it doesn't look like the state is getting set correctly with the imported array.
So, this is what my file looks like right now (Any help would be greatly appreciated!):
import React from 'react';
import endpoints from './endpoints.json';
class Link extends React.Component{
constructor(props){
super(props);
this.state = {
error: null,
isLoaded: false,
myData: []
};
}
componentDidMount() {
let myData = endpoints.map((data, key) => {
console.log(endpoints);
console.log(endpoints[0].key);
return(
<a className="aLink" href={endpoints.link} key={endpoints.key} >{endpoints.name}</a>
)
})
this.setState({myData: myData});
console.log(this.state.myData);
}
render() {
const { error, isLoaded } = this.state;
if (error) {
return <div className="errorM">Error: {error.message}</div>;
} else {
return(
<div>
{this.state.myData}
</div>
)
}
}
}
export default Link;
You seem to be trying to render from the initial response (endpoints) rather than the map value (data). Change
href={endpoints.link} key={endpoints.key} >{endpoints.name}
to
href={data.link} key={data.key} >{data.name}
Well, this was one of those classic, ask a question and then immediately figure out the answer. Basically, where I'm mapping each item, I set an argument called "data". Instead of calling "endpoints.xxx" it should be "data.xxx" for everything. Then, everything renders fine. :)
I have set up a React Frontend with a Node backend for an app I am trying to make. I have successfully created a server which is hosting my data, which I can then access and receive into my React Frontend. I am able to console.log the data I want and successfully saved it to the state (I think?). My issue is that I can't seem to actually pass the information contained in State into the child component.
Units.js
import UnitsCard from "./InfoCardUnits";
import React, { Component } from "react";
const axios = require("axios");
class Units extends Component {
constructor(props) {
super(props);
this.state = {
units: []
};
}
fetchData() {
axios
.get("http://localhost:3001/allData/units")
.then(response => {
// handle success
// console.log("Success");
this.setState({ units: response.data });
})
.catch(error => {
// handle error
console.error(error);
});
}
componentDidMount() {
this.fetchData();
}
render() {
// this console.log will show the data I want to send as props into my child component.
console.log(this.state.units[0]);
return <UnitsCard props={this.state.units[0]} />;
}
}
export default Units;
InfoUnitCard.js
import "../index.css";
function UnitsCard(props) {
// this console.log will show the "props" information that I want to use in my unit card. But the information itself won't actually show in the browser.
console.log(props);
return (
<div className="card">
<h2>{props.name}</h2>
<h2>{props.category}</h2>
<h2>{props.inf_melee}</h2>
</div>
);
}
export default UnitsCard;
When I console.log the state in either of the components it successfully shows the information I am trying to send. But I can't actually get that information to render. Any help or insights would be much appreciated.
EDIT: This has been resolved, thanks very much to everyone who chipped in an answer.
Avoid passing props in via the props keyword. Instead, consider making the following changes to your code:
render() {
// Get unit or empty object (makes code more readable in next step)
const unit = this.state.units[0] || {};
// Pass each piece of unit data in as a separate prop
return <UnitsCard
name={unit.name}
category={unit.category}
inf_melee={unit.inf_melee} />;
}
Alternatively, you could use the "spread" syntax available with ES6 to make this a little more concise:
render() {
// Get unit or empty object (makes code more readable in next step)
const unit = this.state.units[0] || {};
// Use spread operator to simplify passing of props to UnitsCard
return <UnitsCard {...unit} />;
}
Every thing you pass in the child component will be available in props object in the child component. In your case you are passing a 'props' to props object. This should be available as this.props.props.keyname. try changing your child component as follow.
function UnitsCard(props) {
// this console.log will show the "props" information that I want to use in my unit card. But the information itself won't actually show in the browser.
console.log(props);
return (
<div className="card">
<h2>{props.props.name}</h2>
<h2>{props.props.category}</h2>
<h2>{props.props.inf_melee}</h2>
</div>
);
}
You named your props props, so you can access to it with below code:
console.log(props.props);
you can pass like with a different name:
<UnitsCard child={this.state.units[0]} />
Then access to props with props.child, so your code will change to:
<h2>{props.child.name}</h2>
Following the React AJAX example i have created a JSX file which purpose is to fetch and render a movie.
For all i know, i am doing everything right here.
When i console.log the data in my render function i get 2 results:
Undefined
Object (which is the one i need, so this one is perfect)
How can i filter out the Undefined row without doing some if/else logic in my render function?
Iterating over the result will, of course, result in an error the first time, which will crash my application.
What's the best way to handle this?
EDIT: Maybe the app gets rendered before the Axios call is done in which case i am forced to do an if/else statement?
Heres my JSX file:
import React from "react";
import axios from "axios";
export default class NetflixHero extends React.Component {
constructor() {
super();
this.state = {
movie: []
}
}
}
componentDidMount() {
const apiKey = 'xxxxxxxx';
let requestUrl = 'https://api.themoviedb.org/3/' + this.props.apiAction + '&api_key=' + apiKey;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
//Fires twice. Returns Undefined and an Object
console.log(this.state.movie[0]);
return(
<div></div>
)
}
Check the state inside the render method. With this approach you can render a loading screen:
import React from "react";
import axios from "axios";
export default class NetflixHero extends React.Component {
constructor() {
super();
this.state = {
movie: []
}
}
}
componentDidMount() {
const apiKey = '87dfa1c669eea853da609d4968d294be';
let requestUrl = 'https://api.themoviedb.org/3/' + this.props.apiAction + '&api_key=' + apiKey;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
//Loading...
if( this.state.movie[0] === undefined ) {
return <div>Loading...</div>
}
//Loaded successfully...
return(
<div> Movie loaded... [Do action here] </div>
)
}
Explanation
Every time the state changes, a re-render will be triggered. The first time, your Component is constructed with this.state.movie = []. After that, componentDidMount() is triggered, which changes your state. This is triggering the render method a second time.
Is your post crossdomain?
If your request is a CORS (Cross-origin resource sharing) request it will be preceded with a OPTIONS (pre-flight) request. Have a look at CORS and more specifically to the pre-flighted requests.
Basically the OPTIONS request is used to check if you are allowed to perform the GET/POST request from that domain and what headers can be used for that request. This is not axios specific.