May 21, 201610 yr Strange situation happened. I tried to connect remote server with Ajax in React. I use webpack-dev-server. Console shows status 200 OK, but says "data is undefined" and "connection with server is not secure". I can't understand what happening and for 2 days try lot's of ways to get data but without any results. Any ideas will be greatly appreciated! First way. const UserGist = React.createClass({ getInitialState: function() { return { username: '' }; }, componentDidMount: function() { $.ajax({ url: this.props.serverPath, dataType: 'jsonp', cache: false, success: function(data) { const first = data[0]; this.setState ({ username:first.title }); console.log(first); }.bind(this), error: function(xhr, status, err) { console.error(this.props.serverPath, status, err.toString()); }.bind(this) }); }, render:function() { return ( <div> {this.state.username} </div> ); } }); export default UserGist; Second way. componentDidMount:function() { this.request = $.ajax({ url:this.props.serverPath.bind(this), dataType:'jsonp'}) .then(data => { const title = data[0]; this.setState ({ username:title.title, login:title.link }); }); }.bind(this), Index.jsx import UserGist from './official_ajax.jsx'; class App extends React.Component { render () { return ( <div> <p>Hello React!</p> <TestSource path = "USA" /> <UserGist serverPath = "//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?" /> <AwesomeComponent /> </div> ); } } ReactDOM.render(<App />, document.getElementById("app"));
May 22, 201610 yr Why are you using jQuery inside of React? If you are using jQuery just for Ajax then it's totally unnecessary. You should look at something like Super Agent instead as this library specifically abstracts the horrible native XHR. https://github.com/visionmedia/superagent I'd also take out the whole ajax functionality from the component and move it into it's own module, it's better not to mix in so much logic to components. And avoid ES6 Classes whenever possible, if you are not changing any state in the component (State should always live in a store and passed down via props) or component lifecycle hooks (componentWillMount, componentDidMount etc) then you can use a functional component. for example You have this class App extends React.Component { render () { return ( <div> <p>Hello React!</p> <TestSource path = "USA" /> <UserGist serverPath = "//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?" /> <AwesomeComponent /> </div> ); } } Can be replaced with const App = () => ( <div> <p>Hello React!</p> <TestSource path = "USA" /> <UserGist serverPath = "//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?" /> <AwesomeComponent /> </div> ); When you do need lifecycle hooks then favour React.createClass rather than Class. ES6 classes have an array of issues, but specifically for react you get some weird things happening like 'this' not being autobound to the component. Also I just noticed in your 'second way' you are importing the file from 'first way' and your componentDidMount function in the 'second way' is not nested within any component at all? Edited May 22, 201610 yr by rbrtsmith
May 22, 201610 yr ...also, the data returned is an object not an array, there is no data[0]. console.log(data) // => Object { title: "LS 2008 Pool", link: "https://www.flickr.com/groups/ls200…", description: "Dette er ei gruppe på Flickr, der a…", modified: "2008-07-31T06:31:53Z", generator: "https://www.flickr.com/", items: Array[20] } so either use the data object itself username: data.title or if you want the first item in data.items const first = data[0]; // <= change this const [first] = data.items; // <= to this
May 22, 201610 yr Author Why are you using jQuery inside of React? If you are using jQuery just for Ajax then it's totally unnecessary. You should look at something like Super Agent instead as this library specifically abstracts the horrible native XHR. https://github.com/visionmedia/superagent I'd also take out the whole ajax functionality from the component and move it into it's own module, it's better not to mix in so much logic to components. And avoid ES6 Classes whenever possible, if you are not changing any state in the component (State should always live in a store and passed down via props) or component lifecycle hooks (componentWillMount, componentDidMount etc) then you can use a functional component. for example You have this class App extends React.Component { render () { return ( <div> <p>Hello React!</p> <TestSource path = "USA" /> <UserGist serverPath = "//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?" /> <AwesomeComponent /> </div> ); } } Can be replaced with const App = () => ( <div> <p>Hello React!</p> <TestSource path = "USA" /> <UserGist serverPath = "//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?" /> <AwesomeComponent /> </div> ); When you do need lifecycle hooks then favour React.createClass rather than Class. ES6 classes have an array of issues, but specifically for react you get some weird things happening like 'this' not being autobound to the component. Also I just noticed in your 'second way' you are importing the file from 'first way' and your componentDidMount function in the 'second way' is not nested within any component at all? Robert, thank you very much for answer! Superagent is very powerful tool. I tried to use native Fetch API with jsonp but it seems doesn't work. const User_fetch = React.createClass({ getInitialState:function() { return { username:'', login:'' }; }, componentDidMount:function() { fetchJsonp("//api.flickr.com/services/feeds/groups_pool.gne?id=807213@N20&lang=en-us&format=json&jsoncallback=?", { method: 'get', dataType:'jsonp', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }) .then((response) => { return response.json() }) .then((responseData) => { return responseData; }) .then((data) => { const [, first] = data.items; this.setState({ username: first.title }); }).catch(ex => { console.log('parsing failed', ex) }) .done(); }, render:function() { return ( <div> {this.state.username} </div> ); } }); export default User_fetch; fetchJsonp is undefined. Also i found react-fetch but didn't try. "Take out the whole ajax functionality from the component and move it into it's own module" - you mean plugins like superagent? "State should always live in a store and passed down via props" - you mean flux or redux?
May 22, 201610 yr Author ...also, the data returned is an object not an array, there is no data[0]. console.log(data) // => Object { title: "LS 2008 Pool", link: "https://www.flickr.com/groups/ls200…", description: "Dette er ei gruppe på Flickr, der a…", modified: "2008-07-31T06:31:53Z", generator: "https://www.flickr.com/", items: Array[20] } so either use the data object itself username: data.title or if you want the first item in data.items const first = data[0]; // <= change this const [first] = data.items; // <= to this Wynn, thank you very much for pointing to my mistake with json object and es6 destructuring assignment. I tried with objects also const {title} = data; and this works perfect! Edited May 22, 201610 yr by fleur
May 23, 201610 yr I'd advise against JSONP and use CORS instead. Superagent, and I believe jQuery's ajax abstraction both use CORS under the hood to allow cross-domain requests. There's a number of issues with JSONP which has always been a bit of a hack around the same origin issues. CORS allows for more HTTP methods than JSONP that only allows GET requests, CORS also has better error handling, a quick Google will explain the differences in more detail.
May 23, 201610 yr For state it depends, you don't always need a flux architecture or to use Redux, it depends on the scope and complexity of your project. But you can just create an object that sits in it's own ES6 module, import that into the root component and pass it down the component tree as props instead of having local state it is all local. This is one of the principles of Redux, but if you want to enforce immutability and some other good things then Redux will help you with that. In my opinion Redux is the best flux pattern around at the moment, traditional flux, I feel makes things more complex and difficult to reason about than Redux. When I say taking things out of the components I mean making separate ES6 modules for them and just importing them in to that component. Not only does this clean things up, it also allows you to re-use that function as it is not directly tied to your component. I also advise that you do propType checks on your components, it has a number of benefits. Edited May 23, 201610 yr by rbrtsmith
May 25, 201610 yr Author I tried native Fetch API to get this json like fetch("https://davidwalsh.name/demo/arsenal.json", { credentials: 'include' }) .then((response) => { return response.json() }) .then((json) => { const [first] = json; console.log(first); this.setState({ username: first.name }); }).catch(ex => { console.log('parsing failed', ex) }); }, and received "parsing failed TypeError: NetworkError when attempting to fetch resource". Tried fetch polyfill but also failed. Json loader also doesn't work. I found the same issue as mine So.. I think only jQuery and Superagent will really work. I wonder if there is any chance to make fetch (or fetch polyfill) work with webpack.
May 26, 201610 yr You are probably getting CORS errors. Just use superAgent. Webpack and any another module bundler has no impact on what library or functions you write. All they do at their core is bundle JavaScript modules so I've got no idea where your notion 'I wonder if there is any chance to make fetch (or fetch polyfill) work with webpack.' comes from. Edited May 26, 201610 yr by rbrtsmith
May 26, 201610 yr Author Thank you! I was surprised why l can't load local json files with fetch and json loader.
May 26, 201610 yr Strange, CORS is only for cross-domain requests, you should be able to just fetch local ones directly. Although in your example above you are fetching from an external domain. Edited May 26, 201610 yr by rbrtsmith
May 26, 201610 yr Author I was surprised too. I downloaded json-loader via npm. (But i don't see it in package.json). Add line in webpack.config.js module : { loaders : [ { test : /\.jsx?/, exclude: /node_modules/, include : APP_DIR, loader : 'babel' }, { test: /\.json$/, loader: 'json' } ] }, Then i added above text_fetch.jsx line var json = require("json!./test_json.json"); test_json.json in the same folder as text_fetch.jsx And trigger the local file in test_fetch.jsx fetch('/test_json.json') .then((response) => { return response.json() }) .then((json) => { console.log('parsed json', json); const [first] = json; console.log(first); this.setState({ username: first.name }); }).catch(ex => { console.log('parsing failed', ex) }); }, And every time i got Network Error. I don't understand the reason.
May 26, 201610 yr Why are you requiring the JSON file like that? Are you pulling it directly into your bundled JS or are you loading it via the Fetch promise API? It looks like you are trying to do both here. I am not familiar with the fetch API but check your path, as that path is relative to your root dir... Edited May 26, 201610 yr by rbrtsmith
May 27, 201610 yr You should only need to import whatwg-fetch as a polyfill. I've used this before briefly and this is a working example that reads data from a json file: public/contacts.json [ { "name": "Lyndsey Browning", "email": "lbrowning86@somewhere.com" }, { "name": "Dan Abramov", "email": "gaearon@somewhere.com" }, { "name": "Pete Hunt", "email": "floydophone@somewhere.com" } ] app container: import React, { Component, PropTypes } from 'react'; import ContactsApp from './ContactsApp'; import 'whatwg-fetch'; class ContactsAppContainer extends Component { constructor() { super(); this.state = { contacts: [] } } componentDidMount() { fetch('./public/contacts.json') .then((response) => response.json()) .then((responseData) => { this.setState({ contacts: responseData }); }) .catch((error) => { console.log('Error fetching and parsing data', error); }); } render() { return ( <ContactsApp contacts={this.state.contacts} /> ); } } export default ContactsAppContainer; I should add that my folder structure looks like this: public/ -- contacts.json src/ |-- js/ |-- components/ |-- ContactsAppContainer.js |-- etc... -- main.js webpack.config var webpack = require('webpack'); /* * Default webpack configuration for development */ var config = { devtool: 'eval-source-map', entry: __dirname + "/src/js/main.js", output: { path: __dirname + "/build", filename: "bundle.js" }, module: { loaders: [ { test: /\.js?$/, exclude: /node_modules/, loader: 'babel' }, { test: /\.css$/, loaders: ['style', 'css'] } ] }, postcss: [ require('autoprefixer') ], plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ], devServer: { colors: true, historyApiFallback: true, post: process.env.PORT||8080, inline: true, hot: true }, } Edited May 27, 201610 yr by Lyndsey Edited to add folder structure
May 27, 201610 yr @@Lyndsey, I'd recommend you favour React.createClass over ES6 class as it comes with a load of boilerplate nonsense like the constructor, super stuff you see, and any custom methods you add to your component do not get autobinding so you have to explicitly bind this to each function. ugh. import React, { Component, PropTypes } from 'react'; import ContactsApp from './ContactsApp'; import 'whatwg-fetch'; Should be // might as well just import the whole library, it comes with proptypes already but I see your component has no props anyway... import React from 'react'; import ContactsApp from './ContactsApp'; // I see you referencing this as fetch but it's not bound to anything in the code above. I do wish ES6 had implicit module naming like it does for object key/values. import fetch from 'whatwg-fetch'; Hope this helps Edited May 27, 201610 yr by rbrtsmith
May 27, 201610 yr Author Why are you requiring the JSON file like that? Are you pulling it directly into your bundled JS or are you loading it via the Fetch promise API? It looks like you are trying to do both here. I am not familiar with the fetch API but check your path, as that path is relative to your root dir... Robert, thank you very much. I mix tools. I think there is my mistake. I need to start a new project and try fetch there.
May 27, 201610 yr Author Lyndsey, thank you very much for this example. And for webpack.config too! Tomorrow i will set project from scratch like in your structure and will play with your code. It's only way to avoid mess in my code.
May 28, 201610 yr @@Lyndsey, I'd recommend you favour React.createClass over ES6 class as it comes with a load of boilerplate nonsense like the constructor, super stuff you see, and any custom methods you add to your component do not get autobinding so you have to explicitly bind this to each function. ugh. import React, { Component, PropTypes } from 'react';import ContactsApp from './ContactsApp';import 'whatwg-fetch';Should be // might as well just import the whole library, it comes with proptypes already but I see your component has no props anyway...import React from 'react';import ContactsApp from './ContactsApp';// I see you referencing this as fetch but it's not bound to anything in the code above. I do wish ES6 had implicit module naming like it does for object key/values.import fetch from 'whatwg-fetch';Hope this helps Hey Robert, I'm pretty sure we don't need to explicitly name the whatwg-fetch import since it's simply a polyfill for the native window.fetch method. Any browsers not supporting it will automatically pick up the polyfill as far as I know. I now favour react.createClass. My example code was taken from a tutorial I followed. I'm also adopting pure functional components wherever necessary and following Eric Elliott's single React instance method. It's a nice approach.
May 28, 201610 yr Hey Robert, I'm pretty sure we don't need to explicitly name the whatwg-fetch import since it's simply a polyfill for the native window.fetch method. Any browsers not supporting it will automatically pick up the polyfill as far as I know. Ahh, good to know. We just use SuperAgent here for all XHR stuff it's great, and because it creates all the proper CORS headers in the request it allows you to do more than just GET requests
June 4, 201610 yr Author Every time i get the same error "Error fetching and parsing data SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data" I tried everything, don't know what to do else)) Fetch don't work even with whatwg in FF Edited June 4, 201610 yr by fleur
June 4, 201610 yr What url are you trying?There's nothing wrong with jQuery, SuperAgent or Fetch, they all work as expected.The problem lies with the urls you are using:The first url in this thread is returning JSONP but even if you requested JSON (by using nojsoncallback=1) that Flickr endpoint is not CORS enabled.The second David Walsh url you've tried is not CORS enabled either. The error you've posted above suggests that the response isn't valid JSON. When making cross-origin JSON requests, unless the response has the header "Access-Control-Allow-Origin: *" you will receive an error.
June 4, 201610 yr Author Wynn, thank you very much for info! I just tried to make simple request with Lyndsey's JSON. I put all loaders and polyfill but it doesn't work. I tried all! Here is my files. test.json [ { "name": "Lyndsey Browning", "email": "lbrowning86@somewhere.com" }, { "name": "Dan Abramov", "email": "gaearon@somewhere.com" }, { "name": "Pete Hunt", "email": "floydophone@somewhere.com" } ] feth.js require('./test.json'); import 'whatwg-fetch'; const User_fetch = React.createClass({ getInitialState:function() { return { username:[] }; }, componentDidMount:function() { fetch('./test.json') .then((response) => response.json()) .then((responseData) => { console.log('parsed json', responseData); this.setState({ username: responseData }); }).catch((error) => { console.log('Error fetching and parsing data', error); }); }, render:function() { return ( <div> {this.state.username} </div> ); } }); export default User_fetch; main.js require('../../build/index.html'); import User_fetch from './fetch.js'; const App = () => ( <div> <p>Hello React!</p> <User_fetch /> </div> ); ReactDOM.render(<App />, document.body); webpack.config var webpack = require('webpack'); var path = require('path'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var config = { devtool: 'eval-source-map', entry: ['whatwg-fetch', __dirname + "/src/js/main.js"] , output: { path: __dirname + "/build", filename: "bundle.js" }, resolve: { extensions: ['', '.js', '.jsx'] }, devServer: { contentBase: './build', hot: true // Activate hot loading }, module : { loaders : [ { test : /\.jsx?/, exclude: /node_modules/, loader : 'babel' }, { test: /\.json$/, loader: 'json' }, { test: /\.html$/, loader: "raw-loader" } ] }, plugins: [ new HtmlWebpackPlugin({ title: 'Testing fetch API with React' }), new webpack.ProvidePlugin({ 'React': 'react', 'ReactDOM': 'react-dom', '$': 'jquery', Promise: 'imports?this=>global!exports?global.Promise!es6-promise', fetch: 'imports?this=>global!exports?global.fetch!whatwg-fetch' }) ] }; module.exports = config; package.json { "name": "fetch-test", "version": "1.0.0", "description": "Testing fetch API", "main": "index.js", "scripts": { "test": "test", "dev": "webpack-dev-server --port 8080" }, "keywords": [ "54321" ], "author": "OlgaMaraeva", "license": "ISC", "devDependencies": { "babel-core": "^6.9.1", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.5.0", "exports-loader": "^0.6.3", "html-loader": "^0.4.3", "html-webpack-plugin": "^2.19.0", "imports-loader": "^0.6.5", "json-loader": "^0.5.4", "react-hot-loader": "^1.3.0", "webpack": "^1.13.1", "webpack-dev-server": "^1.14.1" }, "dependencies": { "es6-promise": "^3.2.1", "react": "^15.1.0", "react-dom": "^15.1.0", "whatwg-fetch": "^1.0.0" } } For now i see on my webpack dev server only words "Hello React". I tried with $.ajax - cjde working but i really wanted to use native Fetch API. Spent so much time - for nothing:)
Create an account or sign in to comment