React JS, load json data then manipulate it - javascript

Started to play around with react, just learning the small syntax stuff at the moment, im stuck on AJAX requests with data displaying.
The console gives me an error saying the local version of test.json cannot be found. It is in the same directory as the header.js file
//header.js
import React from 'react';
import $ from 'jquery';
var theData = [];
var Header = React.createClass({
getInitialState: function() {
return {
data: null
};
},
componentDidMount: function() {
$.ajax({
dataType: 'json',
url: "./test.json",
success: function(data) {
theData.push(data);
console.log(theData);
}
});
},
render: function() {
return (
<div>
<div id="theData" className="theData"></div>
{theData.someValue}
</div>
</div>
);
}
});

test.json might be in the same directory as header.js, but your code is running client-side and the client (browser) has no idea of what test.json is.
Instead, you should define an endpoint in your server-side logic to read the contents of test.json and return it back to the client as a JSON object. In your client-side logic, the URL property in your current XHR should be replaced with the URI to the endpoint.
Side note: your component as-is won't display any data after the XHR is complete. theData will be properly mutated but it won't trigger a component rerender. You should instead associate your XHR response JSON with component state (which you initialized properly in getInitialState), and React will rerender accordingly when its value is modified.
Update with a code example, assuming your server is Express.
On the server:
const fs = require('fs');
const app = ...
app.get('/name-this-endpoint-appropriately', (req, res) => {
const json = fs.readFileSync('test.json', 'utf8');
res.send(json);
});
On the client (with fixes as mentioned in the side note above):
import React from 'react';
import $ from 'jquery';
var Header = React.createClass({
getInitialState: function() {
return {
data: []
};
},
componentDidMount: function() {
$.ajax({
dataType: 'json',
url: "/name-this-endpoint-appropriately",
success: (data) => this.setState({data})
});
},
render: function() {
return (
<div>
<div id="theData" className="theData"></div>
{this.state.data.someValue}
</div>
</div>
);
}
});

Related

React component not rendered

I have a file called App.js which is my application's main component.
I placed a new component in the result returned by that file's render method:
return (
<div>
<AjaxReq />
//many other components
</div>
);
Where AjaxReq is the following method:
'use strict';
var AjaxReq = React.createClass({
loadDoc: function() {
$.ajax({
url: "someUrl",
context: document.body,
success: function(){
$(this).addClass("done");
}
});
},
render: function() {
return (<div>
<p onClick={this.loadDoc}>
Click this to make AJAX request.
</p>
</div>);
}
});
module.exports = AjaxReq;
Unfortunately, this component is not rendered at all in the page.
Are there any issues with my code?
I don't think that this snippet $(this).addClass("done"); does what you might intend to do.
In that context, this refers to the React Component (Virtual DOM), not the actual element in the DOM.
The only way to access a React Component instance outside of React is by storing the return value of ReactDOM.render.
Also by any chance,have you forgotten to import React (var React = require('react') ) into your AjaxReq module?
Like phpcoderx said, not importing React could be causing nothing to render. To add the CSS class like you are trying to do, you would want to do something more like the following (though I don't think this would affect the lack of initial rendering issue you are seeing).
'use strict';
var AjaxReq = React.createClass({
loadDoc: function() {
$.ajax({
url: "someUrl",
context: document.body,
success: function(){
this.setState({ isLoaded: true });
}
});
},
getInitialState: function() {
return { isLoaded: false };
},
render: function() {
var classname = this.state.isLoaded ? 'done' : '';
return (<div className={classname}>
<p onClick={this.loadDoc}>
Click this to make AJAX request.
</p>
</div>);
};
});
module.exports = AjaxReq;

How do I pull items from MongoDB and update them when a new one is entered?

I am trying to build a simple blog with React, Express, MongoDB and Node. But I am still confused on (1) how to correctly make the ajax request to my database and how do I set state and (2) how to properly update the state.
I've tried setting getInitialState by making an AJAX request, but it won't work. Also I don't know if that is best practice. Also, once someone adds a new post, where am I supposed to place the POST and then how do I properly update state?
var React = require('react');
var List = React.createClass({
render: function() {
return (
<div>
<h2>{this.props.postbody}</h2>
</div>
)
}
})
// I'll break this up into smaller components later, but for now I just want
// to know where to put my database entries into the posts array.
// The only field in MongoDB right now is postbody.
var Home = React.createClass({
getInitialState: function() {
return {
posts: []
}
},
handleClick: function() {
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState = data;
console.log(this.setState);
}
})
},
render: function() {
return (
<div>
{this.state.posts.map(function(post) {
return (
<List postbody={post.postbody}></List>
)
})}
</div>
)
}
})
setState is a function, not a property to be set on this. You should do this.setState(data)

loading json data from local file into React JS

I have a React component and I want to load in my JSON data from a file. The console log currently doesn't work, even though I'm creating the variable data as a global
'use strict';
var React = require('react/addons');
// load in JSON data from file
var data;
var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.open("get", "data.json", true);
oReq.send();
function reqListener(e) {
data = JSON.parse(this.responseText);
}
console.log(data);
var List = React.createClass({
getInitialState: function() {
return {data: this.props.data};
},
render: function() {
var listItems = this.state.data.map(function(item) {
var eachItem = item.works.work;
var photo = eachItem.map(function(url) {
return (
<td>{url.urls}</td>
)
});
});
return <ul>{listItems}</ul>
}
});
var redBubble = React.createClass({
render: function() {
return (
<div>
<List data={data}/>
</div>
);
}
});
module.exports = redBubble;
Ideally, I would prefer to do it something like this, but it's not working - it tries to add ".js" onto the end of the filename.
var data = require('./data.json');
Any advice on the best way, preferably the "React" way, would be much appreciated!
I was trying to do the same thing and this is what worked for me (ES6/ES2015):
import myData from './data.json';
I got the solution from this answer on a react-native thread asking the same thing: https://stackoverflow.com/a/37781882/176002
The simplest and most effective way to make a file available to your component is this:
var data = require('json!./data.json');
Note the json! before the path
You are opening an asynchronous connection, yet you have written your code as if it was synchronous. The reqListener callback function will not execute synchronously with your code (that is, before React.createClass), but only after your entire snippet has run, and the response has been received from your remote location.
Unless you are on a zero-latency quantum-entanglement connection, this is well after all your statements have run. For example, to log the received data, you would:
function reqListener(e) {
data = JSON.parse(this.responseText);
console.log(data);
}
I'm not seeing the use of data in the React component, so I can only suggest this theoretically: why not update your component in the callback?
Install json-loader:
npm i json-loader --save
Create data folder in src:
mkdir data
Put your file(s) there.
Load your file:
var data = require('json!../data/yourfile.json');
If you have couple of json files:
import data from 'sample.json';
If you were to dynamically load one of the many json file, you might have to use a fetch instead:
fetch(`${fileName}.json`)
.then(response => response.json())
.then(data => console.log(data))
My JSON file name: terrifcalculatordata.json
[
{
"id": 1,
"name": "Vigo",
"picture": "./static/images/vigo.png",
"charges": "PKR 100 per excess km"
},
{
"id": 2,
"name": "Mercedes",
"picture": "./static/images/Marcedes.jpg",
"charges": "PKR 200 per excess km"
},
{
"id": 3,
"name": "Lexus",
"picture": "./static/images/Lexus.jpg",
"charges": "PKR 150 per excess km"
}
]
First , import on top:
import calculatorData from "../static/data/terrifcalculatordata.json";
then after return:
<div>
{
calculatorData.map((calculatedata, index) => {
return (
<div key={index}>
<img
src={calculatedata.picture}
class="d-block"
height="170"
/>
<p>
{calculatedata.charges}
</p>
</div>
You could add your JSON file as an external using webpack config. Then you can load up that json in any of your react modules.
Take a look at this answer
If you want to load the file, as part of your app functionality, then the best approach would be to include and reference to that file.
Another approach is to ask for the file, and load it during runtime. This can be done with the FileAPI. There is also another StackOverflow answer about using it:
How to open a local disk file with Javascript?
I will include a slightly modified version for using it in React:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null
};
this.handleFileSelect = this.handleFileSelect.bind(this);
}
displayData(content) {
this.setState({data: content});
}
handleFileSelect(evt) {
let files = evt.target.files;
if (!files.length) {
alert('No file select');
return;
}
let file = files[0];
let that = this;
let reader = new FileReader();
reader.onload = function(e) {
that.displayData(e.target.result);
};
reader.readAsText(file);
}
render() {
const data = this.state.data;
return (
<div>
<input type="file" onChange={this.handleFileSelect}/>
{ data && <p> {data} </p> }
</div>
);
}
}

Halt React from rendering/clearing state of a view until animations/callback is complete?

So i've got a react app using react router. The router hooks up to a nav that updates the same component view for 4 different routes - 2012, 2013, 2014, 2015. Each route triggers an API call that pulls in some JSON, and the component is re-rendered with the new state.data.
What i'd like to do is be able to fade out the view, then call the API, then fade in the new view with the new state.data.
The issue i have at the moment, is the view is rendering blank before it has time to fade out, then the new data is pulled in, and the fade in transition is triggered once the data is loaded.
Are there any hooks i can tap into that'll stop the this.state.data clearing before view has faded out?
/** #jsx React.DOM */
var React = require('react');
var Router = require('react-router');
var Reqwest = require('reqwest');
var StreamItem = require('./StreamItem.jsx');
var Stream = React.createClass({
mixins: [ Router.State ],
getInitialState: function() {
return {
data: null
}
},
readFromAPI: function(url, successFunction) {
Reqwest({
url: url,
type: 'json',
method: 'get',
contentType: 'application/json',
success: successFunction,
error: function(error) {
console.error(url, error['response']);
location = '/';
}
});
console.log('read api called');
},
componentWillMount: function () {
this.readTweetsFromAPI();
},
readTweetsFromAPI: function() {
var self = this;
this.readFromAPI(this.getPath(), function(tweets) {
setTimeout(function() {
self.setState({
data: tweets
});
// state of page transitions is kept a level up to include
// other components. This function toggles a boolean to true,
// which toggles a css class transition on the parent
// component.
self.props.fadeInPage();
}, 500);
}.bind(this));
},
render: function() {
if (this.state.data) {
var tweetItems = this.state.data.map(function(tweet, index) {
return <StreamItem key={tweet.id} tweet={tweet}/>
}.bind(this));
}
return (
<div className="tweet-list__archive">
{tweetItems}
</div>
);
}
});
module.exports = Stream;

JestJs thinks ajax is not called

I'm having a lot of trouble getting a simple jest test to work. Jest insists that my Ajax call is not happening, with the error message:
FAIL authTest.js (1.828s)
● Authentication: Logging In › it Doesn't currently have a logged in user
- Expected Function to be called with { url : 'api/loggedin', type : 'GET', error : <jasmine.any(function Function() { [native code] })>, success : <jasmine.any(function Function() { [native code] })> }.
at Spec.<anonymous> (/Users/ritmatter/reps/spec/authTest.js:13:20)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
The code being tested is in a file called auth.jsx, and it looks like this:
loggedIn: function() {
return $.ajax({
url: 'api/loggedin',
type: 'GET',
error: function(xhr, status, err) {
return false;
}.bind(this),
success: function(data) {
return true;
}.bind(this),
});
},
The test looks like this:
/** #jsx React.DOM */
"use strict";
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
describe('Authentication: Logging In', function() {
it('Doesn\'t currently have a logged in user', function () {
var $ = require('jquery');
jest.dontMock('../js/auth.jsx');
var auth = require('../js/auth.jsx');
auth.loggedIn();
expect($.ajax).toBeCalledWith({
url: 'api/loggedin',
type: 'GET',
error: jasmine.any(Function),
success: jasmine.any(Function)
});
});
});
Any idea why jest would think that this is not getting called? I've been looking around, and it seems like there are some bugs with respect to dontMock() and mock().
As Wagner mentioned, you need to require jquery globally, outside of your test. Your component is using the global version of $, so adding var $ = require('jquery') doesn't do anything in terms of adding jquery to the global variable $.
You also did not mock the ajax call.
When testing react, I avoid issues with loading jquery by simply redefining $:
window.$ = {ajax: jest.genMockFunction()}
So, as long as you don't need jquery for anything else other than an ajax call, this one line will simulate the jquery root and mock the ajax call.
I had a similar problem with a React component that invoked ajax on initialisation.
What I found is that expect on $ only work if you require jquery outside the it method.
My React component and test case are like these (They are ES6 but you can get the idea)
import React from 'react'
import $ from 'jquery'
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {things:[]};
}
componentWillMount() {
$.ajax({
url: 'http://localhost:3000/things',
success: (result) => this.setState(result),
error: (ex) => console.log(ex)
})
}
render() {
//stuff
}
}
and the test
jest.dontMock('../components/MyComponent')
import React from 'react'
import TestUtils from 'react-addons-test-utils'
import $ from 'jquery'
const Wall = require('../components/MyComponent');
describe('MyComponent', () => {
it('calls the things end point', () => {
const myComp = TestUtils.renderIntoDocument(<MyComponent />)
expect($.ajax).toBeCalledWith({
url: 'http://localhost:3000/things',
success: jasmine.any(Function),
error: jasmine.any(Function)
})
});
});

Categories

Resources