ReactJS: Uncaught ReferenceError: [function] not defined, July 2017 - javascript

Attempting to turn a JSON file into an array, then randomly selecting 5 items from it.
I think the issue is my render/return statement at the bottom of ImageContainer.js, but I'm a ReactJS n00b, so it could be anything.
Any help or advice is greatly appreciated.
Console Error
compiled.js:31562 Uncaught ReferenceError: selectImages is not defined
at ImageContainer.render (compiled.js:31562)
at compiled.js:20195
at measureLifeCyclePerf (compiled.js:19475)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (compiled.js:20194)
at ReactCompositeComponentWrapper._renderValidatedComponent (compiled.js:20221)
at ReactCompositeComponentWrapper.performInitialMount (compiled.js:19761)
at ReactCompositeComponentWrapper.mountComponent (compiled.js:19657)
at Object.mountComponent (compiled.js:4009)
at ReactDOMComponent.mountChildren (compiled.js:24150)
at ReactDOMComponent._createInitialChildren (compiled.js:21126)
ImageContainer.js - pulls img info from a .JSON file, randomly selects 5 to render
import React from 'react';
import ReactDOM from 'react-dom';
import { Image } from './Image';
export class ImageContainer extends React.Component {
constructor(props) {
super(props);
this.numImages = 5;
this.state = {
data: []
};
}
componentDidMount() {
$.ajax({
url: '../app/data/namegame-data.json',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('#GET Error', status, err.toString());
}.bind(this)
});
}
shuffleArray(array) {
let i = array.length - 1;
for (; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
selectImages({ data }) {
const shuffledImages = shuffleArray(images);
return (
shuffledImages.map((data, idx) => {
for (let i = 0; i < this.numImages; i++) {
if (this.state.data.length > 0) {
return <Image key={idx} name={data.name} src={data.src} />;
}
}
})
);
}
render() {
return {selectImages};
}
}
Image.js - just the image HTML
import React from 'react';
export class Image extends React.Component {
render() {
const key = this.props.id;
const name = this.props.name;
const src = this.props.src;
return (
<picture>
<img id="pic-template" className="pic" src="{src}" name="{name}" key="id" />
</picture>
);
}
}
[Main].js - most of the HTML is here, just including the import bits
import React from 'react';
import ReactDOM from 'react-dom';
import { ImageContainer } from './ImageContainer';
export default class NameGame extends React.Component {
render () {
return (
...
<div id="images-wrap">
<ImageContainer />
</div>
...
);
}
}
Dependencies
{
...
"dependencies": {
"json-loader": "^0.5.4",
"react": "^15.6.1",
"react-dom": "^15.6.1"
},
"devDependencies": {
"css-loader": "^0.28.4",
"extract-text-webpack-plugin": "^2.1.2",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^2.2.0",
"webpack-dev-server": "^2.5.0"
}
}

Here is the updated (mostly) working version of ImageContainer.js, which incorporates Danillo's changes and solves the array problem that followed. The issues were mainly related to missing "this." and "this.state."
ImageContainer.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Image } from './Image';
export class ImageContainer extends React.Component {
constructor(props) {
super(props);
this.numImages = 5;
this.state = {
data: []
};
}
componentWillMount() {
$.ajax({
url: './app/data/namegame-data.json',
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('#GET Error', status, err.toString());
}.bind(this)
});
}
shuffleArray(array) {
let i = array.length - 1;
for (; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
selectImages() {
const shuffledImages = this.shuffleArray(this.state.data);
return (
shuffledImages.map((data, idx) => {
for (let i = 0; i < this.numImages; i++) {
return <Image key={idx} name={this.state.data.name} src={this.state.data.src} />;
}
})
);
}
render() {
return (
<div>
{this.selectImages()};
</div>
)
}
}

i think "this" is where it goes wrong ;):
selectImages is part of the ImageContainer class,
meaning the function is scoped within this Class, so it should be this.selectImages
In the render you call selectimages
render() {
return {this.selectImages()}; <-- its a function so u should call it as a function
}
Remember selectimage should return a react component, but if it will return a array of items that
react will render you should wrap it, you cannot pas multiple react elements to render
so you should something like
render() {
return (
<div> <-- give render 1 react component wrapping the multiple components
{this.selectImages()}; <-- its a function so u should call it as a function
</div>
)
}
selectImages({ data }) <-- where is it getting data from ? i don't think data is needed , only if u gonna pass { data: 'whatever' } to the function
shuffledImages.map((data, idx) => {
data will be the items from the shuffledImages array
})
const shuffledImages = shuffleArray(images); should be ->
const shuffledImages = this.shuffleArray(images);
about your other question
shuffleArray() {
(array) <-- u are mutating the data from the state, when u passed it to the function, u should only change state with setState.
use another shuffle function where u dont mutate the data ,use concat or es6 spread operator [ ...data ] etc .
read: "Correct modification of state arrays in ReactJS"
}
selectImages() {
const shuffledImages = this.shuffleArray(this.state.data); <-- u dont have to pass this.state.data, to the function ,it is accessable within shufflarray -> const shuffledImages = this.shuffleArray();
return (
shuffledImages.map((data, idx) => {
for (let i = 0; i < this.numImages; i++) {
return <Image key={idx} name={this.state.data.name} src={this.state.data.src} />;
}
})
);
}
if ur ajax call fails array is undefined. and u cannot call length on undefined. (thats what the error says)- about ur call
u need to tell ur ajax call what to do, is it a post or get?
add a 'type' (it should be type: 'get', )
or use the shorthand (i see u are using jquery)
$.getJSON('whateverjson file.json').done(function(response) {
console.log("Success");
}).fail(function() {
console.log("Error");
});
About the data ->
u should parse the json setstate({ data: JSON.parse(data) }) or setstate({ data: $.parseJSON(data) });
I must say using jquery, in combination with react, is not something i should recommend, jquery is mostly used for dom manipulation, react uses its virtual dom, and to include the whole jquery library for only the ajax requests is a waste.
try 'fetch' <==
PS look into https://facebook.github.io/react/docs/react-api.html (react docs)
Its also best to pass results of api calls to the component as props, but u will figure that out once u dive into the docs! i hope this helps u, good luck m8

i quickly changed ur code at 00:45 :D, but try something like this, explenation in my other answer. gl!
import React from 'react';
import ReactDOM from 'react-dom';
import { Image } from './Image';
export class ImageContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
numImages: 5
};
}
componentWillMount() {
$.ajax({
url: './app/data/namegame-data.json',
dataType: 'json',
type: 'get',
success: function(data) {
this.setState({data: JSON.parse(data) });
}.bind(this),
error: function(xhr, status, err) {
console.error('#GET Error', status, err.toString());
}.bind(this)
});
}
randomPickFromArray() {
// * as asked: " Attempting to turn a JSON file into an array, then randomly selecting 5 items from it. "
// lets not shuffle, and just random pick 5 items from array,
const { data, numImages } = this.state;
const arr = [];
for (let i = 0; i < numImages; i++) {
arr.push(data[Math.floor(Math.random() * data.length)]);
}
return arr;
}
selectImages() {
const shuffledImages = this.randomPickFromArray();
return shuffledImages.map((item, idx) => <Image key={idx} name={item.name} src={item.src} />);
}
render() {
return (
<div>
{this.selectImages()};
</div>
)
}
}

Related

How do I toggle className for button variable in react functional component?

I have a react functional component that takes a button as a variable, and launches a function on click. The button is a variable because this function is getting imported into another functional component, and the button is created in this external function. I would also like to set and toggle the button class on click, to indicate whether the button/tool is active or not. I have not been able to find a way to toggle the className. In my example I am using the useState hook to set a default value for the class, and then in the click function I am changing the value of the class depending on the value of another useState hook. This does not show any class for the button. I can specify the className as activeClass outside of the click function and it will that class, but can't figure out how to toggle. I have also tried setting the className in the return statement like this: <button className={btnActive ? 'one_class' : 'two_class'} onClick={function}</> but this also does not set the class. What am I doing wrong? This is my first attempt at using the react framework.
import React from 'react';
import { useInput } from '../hooks/useInput';
import {
placesSource,
places_lyr,
}
from "../qgis2web/layers";
import {
increaseThrobberCounter,
decreaseThrobberCounter
}
from "../qgis2web/throbberCounter";
import { addLayer, getErrorMessage, removeLayer } from "../qgis2web/qgis2web";
import { add_geojson_feats } from "../qgis2web/addGeojsonFeatures";
declare const ol, $, toastr;
const GetPlacesControl = ({ map, launchButton }) => {
const [btnActive, setBtnState] = React.useState<boolean>(false);
const [activeClass, setClass] = React.useState("fas fa-globe-americas");
function clear_places_features(map) {
placesSource.clear();
removeLayer(places_lyr);
}
const toggle_get_places = (launchButton) => {
setBtnState((btnActive) => {
setClass((activeClass) => {
if (btnActive) {
setClass("fas fa-ban");
$('html,body').css('cursor', 'default');
map.un('click', runCallback);
clear_places_features(map);
} else {
setClass("fas fa-globe-americas");
$('html,body').css('cursor', 'crosshair');
map.on('click', runCallback);
}
launchButton.className = activeClass;
return launchButton.className;
});
return !btnActive;
});
}
const runCallback = React.useCallback((evt) => {
clear_places_features(map);
$.ajax({
type: "POST",
contentType: "application/json;charset=utf-8",
url: "api/get_places?x=" + evt.coordinate[0] + "&y=" + evt.coordinate[1],
traditional: "true",
datatype: "json",
mimeType: 'application/json',
}).done(function (response) {
const places_feats = new ol.format.GeoJSON().readFeatures(response);
if (places_feats.length == 0) {
toastr.warning('no records found, try again');
} else {
placesSource.addFeatures(places_feats);
map.getView().fit(placesSource.getExtent());
places_lyr.setVisible(true);
addLayer(places_lyr);
}
}).fail(function (jqXHR) {
let msg = getErrorMessage(jqXHR);
toastr.error(msg);
}).always(function () {
decreaseThrobberCounter();
});
}, []);
React.useEffect(() => {
const get_coords_on_click = (evt) => {
let loc_coords = evt.coordinate;
return loc_coords;
};
launchButton.addEventListener('click', toggle_get_places, false);
map.on('singleclick', get_coords_on_click);
return () => {
launchButton.removeEventListener('click', toggle_get_places);
map.un('singleclick', get_coords_on_click);
};
}, []);
return (
<button onClick={launchButton.toggle_get_places}></button>
);
};
export default GetPlacesControl;
Your code does not work
You should try to use useRef and classList.add/remove

Unhandled Rejection (TypeError): Cannot read property 'length' of undefined MERN

I building a todo app using MERN stack. I am new to this new web development stack and I am getting this error
cannot read property 'length'
I am finding it difficult to solve this error. what are the best resources to learn MERN?
Todo.js
import React, { Component } from 'react'
import axios from 'axios';
// eslint-disable-next-line
export class Todo extends Component {
constructor(props) {
super(props)
this.state = {
todos : [],
item : ""
}
}
changeHandler = (event) => {
this.setState({item: event.target.value})
}
clickHandler = (event) => {
event.preventDefault()
console.log(this.state.item)
axios({
method: 'post',
url: 'http://localhost:3000/',
data: {
todo: this.state.item,
}
});
this.setState({item:''})
}
componentDidMount() {
axios.get('http://localhost:3000/').then((response) => {
console.log(response.data.data)
let data = [];
console.log(response.data.data.length)
for(var i =0; i < response.data.data.length; i++){
data.push(response.data.data[i].todo)
}
this.setState({todos: data})
});
}
componentDidUpdate() {
axios.get('http://localhost:3000/').then((response) => {
console.log(response.data.data)
let data = [];
console.log(response.data.data.length)
for(var i =0; i < response.data.data.length; i++){
data.push(response.data.data[i].todo)
}
this.setState({todos: data})
});
}
render() {
return (
<div>
<input type="text" onChange={this.changeHandler}/>
<button type="submit" onClick={this.clickHandler}>add</button>
<div>
<ul>{this.state.todos.map((todo, index) => <li key={index}>{todo}</li>)}</ul>
</div>
</div>
)
}
}
export default Todo
I believe your error is here response.data.data.length
To handle this error, you can put the value in a variable
const responseDataLength = response?.data?.data?.length || 0;
And then use this value inside your loops
This will only handle the error
The cause of the error is still unhandled. You need to figure out why the response data is not what you were expecting it to be.

papaparse not parsing full data

I am making a react app which fetches and displays some CSV file data from the public folder. My react container looks like this:
import React, { Component } from 'react'
import * as Chart from "chart.js";
import { connect } from 'react-redux'
import { Bar } from 'react-chartjs-2'
import * as actions from "../actions"
import * as Papa from 'papaparse'
function mapStateToProps({stats}) {
return {
data: stats.data
}
}
class Stats extends Component {
fetchCsv(fileName) {
return fetch(fileName).then(function (response) {
let reader = response.body.getReader();
let decoder = new TextDecoder('utf-8');
return reader.read().then(function (result) {
return decoder.decode(result.value);
});
});
}
constructor(props){
super(props);
}
async componentDidMount() {
let PlayerArray = [], BallByBallArray = [], MatchArray = [],
PlayerMatchArray = [], SeasonArray = [], TeamArray = [];
let PlayerMatchData = await this.fetchCsv("Player_Match.csv");
Papa.parse(PlayerMatchData, {
complete: function(results) {
console.log("Finished:", results.data);
PlayerMatchArray = results.data;
console.log("entries.length: " + results.data.length);
}
});
}
render() {
return (
<div>
</div>
)
}
}
export default connect(
mapStateToProps, actions
)(Stats)
This is fetching the content of Player_Match.csv file using the fetchCsv function asynchronously and storing it in PlayerMatchData variable.
Then PlayerMatchData is being parsed by papaparse. The issue is that the file has 12700 entries in it and it is only fetching 3776 entries in google chrome.
Also, strangely it is fetching 1660 entries in firefox
Any input will be appreciated. :)
Edit: Link to the data: https://www.kaggle.com/harsha547/indian-premier-league-csv-dataset#Player_Match.csv
Okay, so I got the answer. As told by #SergiuParaschiv reader.read() only reads one chunk of data. So I instead used the fetch function to complete the task. There was no issue in the parsing.
await fetch("Player_Match.csv")
.then(response => response.text())
.then(text => {
PlayerMatchData = text;
// console.log(text)
})
This will do the task.

data becomes undefined after callback in react

trying to pass data from an api call to a component, but after the api call the data becomes undefined. im fairly new at react so any help would be greatly appreciated Thanks! all the classes are below, i didnt include the form componenet but it gets the data just fine
App.js
import React, { Component } from "react";
import axios from "axios";
import ShowtimeList from "./components/ShowtimeList";
import Form from "./components/Form";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
isFetching: true
};
this.getShowtimes = this.getShowtimes.bind(this);
}
getShowtimes(event) {
event.preventDefault();
const startDate = event.target.startDate.value;
const numDays = event.target.numDays.value;
const zipCode = event.target.zipCode.value;
const radius = event.target.radius.value;
const unit = event.target.units.value;
let showtimes = {};
const API_KEY = "<API-KEY>";
const call =
"http://data.tmsapi.com/v1.1/movies/showings?startDate=" +
startDate +
"&numDays=" +
numDays +
"&zip=" +
zipCode +
"&radius=" +
radius +
"&units=" +
unit +
"&api_key=" +
API_KEY;
this.setState({ isFetching: !this.state.isFetching });
axios
.get(call)
.then(function(response) {
console.log(response.data);
showtimes = response.data;
console.log(showtimes);
})
.catch(function(error) {
console.log(error);
});
}
renderShowtimes(showtimes) {
let times = "";
console.log(this.showtimes); ----- Undefined
if (this.showtimes != null) {
times = <ShowtimeList showtimes={this.showtimes} />;
} else {
times = "No Showtimes In Your Area";
}
return times;
}
render() {
return (
<div>
{this.state.isFetching ? (
<Form getShowtimes={this.getShowtimes} />
) : (
this.renderShowtimes()
)}
</div>
);
}
}
export default App;
ShowtimeList.js
import React, { Component } from "react";
import Showtime from "./Showtime";
class ShowtimeList extends Component {
render() {
return (
<ul>
{this.props.showtimes.map(showtime => {
return <Showtime showtime={showtime} />;
})}
</ul>
);
}
}
export default ShowtimeList;
Showtime.js
import React, { Component } from "react";
class Showtime extends Component {
render() {
return <li>{this.props.showtime}</li>;
}
}
export default Showtime;
Use state to store the showtimes and pass it down as props. Inside your state, add showtimes. Then inside your axios call instead of showtimes = response.data;, do a setState. this.setState({showtimes: response.data})
Then do <ShowtimeList showtimes={this.state.showtimes} />
You are not declaring the variable showtimes at the components scope, so that's why this.showtimes will always be undefined.
Anyway, I would recommend to store that data inside your component state.
Also in renderShowtimes you are asking for a showtime argument which you are not passing later when calling the function inside the render method.
You never set showtimes into your state. To fix this:
...
var _this = this;
axios
.get(call)
.then(function(response) {
console.log(response.data);
showtimes = response.data;
_this.setState({ showtimes: showtimes });
console.log(showtimes);
})
.catch(function(error) {
console.log(error);
});
...

React/Redux: How can I pass this state from one component to another file?

I'm a beginner in React and Redux. I've been working on this project where I finally figured out how to store an array as a state. Now, the only problem I'm having is, trying to figure out how to pass that state to another file.
Here are the two files
-Hue.js
-ColorShop.js
In Hue.js, I made an API and saved the contents into an array of objects called productJSON
Hue.js
class Hue extends React.Component {
constructor() {
super();
this.state = {
productJSON: []
};
}
componentWillMount() {
fetch('numberendpoint.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = [] //initialize array to receive json data
for (let i =0; i < data.length; i++) {
colorArray.push(data[i])
}
let productJSON = JSON.stringify(productArray)
this.setState({productJSON: productJSON});
})
}
render() {
return (
<div>
<div className="container2">
{this.state.productJSON}
</div>
</div>
)
}
}
Now, I'm trying to pass productJSON to another file in the same folder, ColorShop.js. I need to replace _colors (which was reading from a static json file) with productJSON.
ColorShop.js
import Hue from './Hue.js'
const TIMEOUT = 100
Hue productJSON={this.state.productJSON} <---- my attempt to get it to work
export default { // I need to replace '_colors' with productJSON
getColors: (cb, timeout) => setTimeout(() => cb(_colors), timeout || TIMEOUT),
}
I don't want to make another class in ColorShop.js, I just want to import this.state.productJSON into it, is that possible? Any pointers are greatly appreciated!!
Update: used the solution suggested by Rahamin. Now I have this code below, all contained within the the "Hue" class. But I'm still getting errors.
import React from 'react'
const TIMEOUT = 100
let productJSON;
class Hue extends React.Component {
constructor() {
super();
this.state = {
products: [],
};
this.getColors = this.getColors.bind(this)
}
componentDidMount() {
fetch('http://tech.work.co/shopping-cart/products.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = []
for (let i =0; i < data.length; i++) {
colorArray.push(data[i])
}
console.log("jsonproduct=" + JSON.stringify(productArray))
productJSON = JSON.stringify(colorArray)
this.setState({productJSON: productJSON});
});
}
render() {
return (
<div>
<div className="container2">
{this.state.productJSON}
</div>
</div>
)
}
}
export default {
getColors: (cb, timeout) => setTimeout(() => cb(({ productJSON: value})), timeout || TIMEOUT), // here is where I am getting an error -- "value" is undefined. I'm not sure I was meant to put "value" there or something else...very new to React so its conventions are still foreign to me.
}
What do you want to do? If the 'other file' is a helper function, you can just pass it a parameter, as you do in any function in any programming language:
From Hue you call colorShop(productsJson) and get back a result that you can render in Hue (colorShop starting with a lowercase character, otherwise React will think it is a component). It seems that the 'other file' can just be a function in the Hue.js file...
ColorShop can also be a component that gets productsJson as a prop and renders it after modification. ColorShop doesn't need to be a class, it can be a functional component. But this doesn't seem to be required by your example.
Here is the code (not complete - see comments) after inserting colorShop as a function into the class component. And you can pass it a value and get a returned value, or have it set the state, whatever you like:
import React from 'react';
const TIMEOUT = 100;
class Hue extends React.Component {
constructor() {
super();
this.state = {
productJSON: []
};
this.getColors = this.getColors.bind(this);
}
componentWillMount() {
fetch('numberendpoint.json')
.then(results => {
return results.json();
}).then(data => {
let colorArray = []; //initialize array to receive json data
for (let i = 0; i < data.length; i++) {
colorArray.push(data[i]);
}
let productJSON = JSON.stringify(productArray);
this.setState({ productJSON: productJSON });
});
}
render() {
// call getColor from wherver you need to call it.
// for example:
// getColors();
return (
<div>
<div className="container2">
{this.state.productJSON}
</div>
</div>
);
}
getColors(cb, timeout) {
setTimeout(() => cb(colors), timeout || TIMEOUT);
// Here you can setState({ productJSON: value });
// Or you can return a value to the caller
}

Categories