I have a .jsx with a parent class and a child, in the parent i initialize the api and stock the json content in a state:
constructor(props) {
super(props);
this.state = {
all: '',
};
}
componentDidMount() {
this.loadApi();
}
loadApi(){
this.setState({ all: myApiGet('https://********') });
}
After that i need to get the "url" of the differents pics for show them on the site. But there is the problem, I get the api json when i load the page and i don't success to re-load the function.
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture = (i) => () => {
// TODO do something with the data
var stock = this.props.all
.then(stock => this.setState({ pictures: stock.content.categories[i].background }))
.catch(error => console.log('home2', error));
}
I try a lot of possibility and check the net but the solution doesn't work for me (or i just doesn't understand them ...)
Thanks for your time :/
Full component:
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
//this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture = (i) => () => {
// TODO do something with the data
var stock = this.props.all
.then(stock => this.setState({ pictures: stock.content.categories[i].background }))
.catch(error => console.log('home2', error));
}
render() {
return (
......
)
}
}
Error message:
The above error occurred in the component:
in ProductItem (created by Home2)
in div (created by Home2)
in div (created by Home2)
in div (created by Home2)
in div (created by Home2)
in main (created by Home2)
in Home2
Consider adding an error boundary to your tree to customize error handling behavior.
You can learn more about error boundaries at https:// fb.me/react-error-boundaries.
react-dom.development.js:9312:5
ReferenceError: props is not defined
Ok i think i see some changes to be made
in your parent component your setting this.state.all to be a promise (the promise returned from your api call)
let's change that to be the actual json from your api call
Parent component:
constructor(props) {
super(props);
this.state = {
all: '',
};
this.loadApi = this.loadApi.bind(this);
}
componentDidMount() {
this.loadApi();
}
loadApi() {
myApiGet('https://********')
.then(all => this.setState({ all }));
}
Child Component:
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
ComponetDidMount() {
apiGetProductPicture(this.props.categorie);
}
componentWillReceiveProps(nextProps) {
if (nextProps.categorie !== this.props.categorie)
{
this.apiGetProductPicture(nextProps.categorie);
}
}
apiGetProductPicture(categorie) {
// TODO do something with the data
if (!this.props.all) return;
var categories = (((this.props.all || {}).stock || {}).content || {}).categories;
if (categories.indexOf(categorie) > -1)
{
this.setState({ pictures: categories[categorie].background }));
}
}
render() {
return (
......
);
}
}
Thanks for your time :/
no worries :)
i se you posted "Lost in the javascriptception"
this and other questions have provided me with enough info to solve your problem, sorry the stackoverflow community was so mean to you, but not all of us are like that.
I would recommend in the future you post more info on your questions, like full code (except sensible stuff), not just parts, the codesanbox was the thing that let me test code and see where the problem was.
Also i f*** up on some of the previous answer, but to be fair i had very limited info to go along with, and most people answering won't test the code for tipos or stuff
version One
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class ProductItem extends React.Component {
constructor(props) {
super(props);
this.state = {
pictures: '',
name: '',
price: '',
json: '',
};
this.apiGetProductPicture = this.apiGetProductPicture.bind(this);
}
componentDidMount() {
this.apiGetProductPicture(this.props.categorie);
}
componentWillReceiveProps(nextProps) {
this.apiGetProductPicture(nextProps.categorie);
}
apiGetProductPicture(categorie) {
// TODO do something with the data
var categories = this.props.send;
categorie = parseInt(categorie, 10);
if (categorie < categories.length) {
console.log(categories[categorie].background);
this.setState({ pictures: categories[categorie].background });
}
}
render() {
return (
<div>
<p>{this.props.name}</p>
<img src={this.state.pictures} />
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
all: "",
categories: []
};
this.loadAPI = this.loadAPI.bind(this);
}
componentDidMount() {
this.loadAPI();
}
loadAPI() {
var test = fetch("https:*******")
.then(test => test.json())
.then(testJson => {
// alert(testJson.content.categories[0].description)
var obs = testJson.content.categories.slice();
// alert(testJson);
this.setState({ categories: obs });
});
}
render() {
return (
<div style={styles}>
<Hello name="CodeSandbox" />
<h1>Products</h1>
{this.state.categories.map( (value, i) => {
return <ProductItem
key={value.uid}
send={this.state.categories}
name={value.description}
categorie={i} />
})}
<h2>Start editing to see some magic happen {"\u2728"}</h2>
</div>
);
}
}
render(<App />, document.getElementById("root"));
My recommended Version
import React from "react";
import { render } from "react-dom";
import Hello from "./Hello";
const styles = {
fontFamily: "sans-serif",
textAlign: "center"
};
class ProductItem extends React.Component {
render() {
return (
<div>
<p>{this.props.name}</p>
<img src={this.props.picture} />
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
all: "",
categories: []
};
this.loadAPI = this.loadAPI.bind(this);
}
componentDidMount() {
this.loadAPI();
}
loadAPI() {
var test = fetch("https:*****")
.then(test => test.json())
.then(testJson => {
// alert(testJson.content.categories[0].description)
var obs = testJson.content.categories.slice();
// alert(testJson);
this.setState({ categories: obs });
});
}
render() {
return (
<div style={styles}>
<Hello name="CodeSandbox" />
<h1>Products</h1>
{this.state.categories.map( (value, i) => {
return <ProductItem
key={value.uid}
picture={value.background}
name={value.description}
categorie={i} />
})}
<h2>Start editing to see some magic happen {"\u2728"}</h2>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Hope this helps you out, don't be so hard on yourself, you know practice makes perfect, also would recommend you follow the react tutorial, to see what react is about, i can seam super hard and weird because it maybe a completely different programming model (it was for me), but when it click it's really cool
Related
CouponStack.js
const Coupon = {
screen: CouponScreen,
params: { updated: null },
navigationOptions: ({ navigation }) => ({
}),
};
const CouponDetail = {
screen: CouponDetailScreen,
navigationOptions: () => ({
}),
};
const CouponStack = createStackNavigator(
{
Coupon,
CouponDetail,
},
{
initialRouteName: 'Coupon',
navigationOptions: {
},
},
);
export default CouponStack;
Index.js
export default class Index extends React.Component {
constructor(props) {
super(props);
this.state = {
typeOne: [],
typeTwo: [],
typeThree: [],
};
}
async componentDidMount() {
}
async shouldComponentUpdate(prevProps) {
if (prevProps.navigation.state.params.updated) {
const { updated } = prevProps.navigation.state.params;
// updated = object
const { typeTwo, typeThree } = this.state;
// typeThree = []
this.setState({
typeTwo: typeTwo.filter((v) => v.id !== updated.id),
typeThree: [...typeThree, updated],
});
}
}
render() {
return (
);
}
}
Detail.js
export default class Detail extends React.Component {
constructor(props) {
super(props);
this.state = {
coupon: this.props.navigation.state.params.coupon,
};
}
backToIndex() {
const updated = this.state.coupon;
this.props.navigation.navigate('Index', { updated });
}
}
What I'm trying to do
When navigating from Detail to Index, I want to setState again.
So, I'm using shouldComponentUpdate in Index.js.
error
[Unhandled promise rejection: Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.]
It looks like Index.js repeatedly calls setState when navigating to Index from Detail.
I would appreciate it if you could tell me how to solve this.
I'm a bit new to React and Firestore and already trying to figure out what is happening for a couple of hours. I Try to make my filter function working with data which I receive from Firestore in APP.js. I pass the data {tasks, searchTerm} to DASHBOARD component. The filter worked before when using state and props, but after replacing the hard-coded data in state with firestore data, it doesn't work anymore and I get the following error when filtering the array in the DASHBOARD component:
Cannot read property 'toLowerCase' of undefined
I've tried to send the data without any filtering directly to TASKS.js and this is working correctly (all the tasks are shown). But as soon as I pass newArray to , it doesn't work anymore.
Also, when logging task.title in tasks.filter function in the DASHBOARD component, it shows all the data (with a little delay because the data is coming from Firestore)
APP.JS -
import React, { Component } from 'react';
import './App.css';
import Dashboard from './Components/Dashboard/Dashboard'
import AddTask from './Components/Tasks/Task/AddTask'
import Navbar from './Components/Navbar/Navbar'
import Searchbar from './Components/Searchbar/Searchbar'
import firebase from './Firebase';
class App extends Component {
constructor(props) {
super(props)
this.ref = firebase.firestore().collection('tasks')
this.state = {
tasks: [],
searchTerm: ""
}
this.handleLikeButton = this.handleLikeButton.bind(this)
this.handleRemoveButton = this.handleRemoveButton.bind(this)
this.addTask = this.addTask.bind(this)
this.handleFilter = this.handleFilter.bind(this)
}
componentWillMount() {
const db = firebase.firestore()
const allTasks = []
db.collection('tasks').onSnapshot(collection => {
const tasks = collection .docs.map(doc => doc.data())
this.setState({ tasks: tasks, searchTerm: "" })
})
}
handleLikeButton = (task) => (e) => {
const tasks = [...this.state.tasks]
const index = tasks.indexOf(task)
tasks[index].likes++
this.setState({
tasks: tasks
})
}
addTask = (taskName) => (e) => {
this.ref.add({
id: Math.floor(Math.random() * 100000000000000),
title: taskName,
likes: 0
})
}
handleRemoveButton = (removingTask) => (e) => {
const tasks = [...this.state.tasks]
const newTasks = tasks.filter(task => removingTask.id !== task.id)
this.setState({
tasks: newTasks
})
}
handleFilter = (searchTerm) => {
this.setState({
searchTerm: searchTerm
})
}
render() {
return (
<div className="App">
<Navbar />
<Searchbar handleFilter={this.handleFilter} />
<AddTask addTask={this.addTask} />
<Dashboard tasks={this.state.tasks} searchTerm={this.state.searchTerm} handleLikeButton={this.handleLikeButton} handleRemoveButton={this.handleRemoveButton}/>
</div>
);
}
}
export default App;
DASHBOARD.JS -
import React, { Component } from 'react'
import Tasks from '../Tasks/Tasks'
class Dashboard extends Component {
constructor(props) {
super(props)
this.filterTasks = this.filterTasks.bind(this)
}
filterTasks = () => {
const tasks = [...this.props.tasks]
const newArray = tasks.filter(task =>
task.title.toLowerCase().indexOf(this.props.searchTerm.toLowerCase()) > -1)
return (
<Tasks tasks={newArray} handleLikeButton={this.props.handleLikeButton} handleRemoveButton={this.props.handleRemoveButton} />
)
}
render() {
return (
<div>
<h2>Dashboard</h2>
{this.filterTasks()}
</div>
)
}
}
export default Dashboard
ADDTASK.JS
import React, { Component } from 'react'
class AddTask extends Component {
constructor(props) {
super(props)
this.state = {
addNewTaskFieldEmpty: true,
taskName: ""
}
this.onChangeHandler = this.onChangeHandler.bind(this)
this.disableButton = this.disableButton.bind(this)
}
onChangeHandler(e) {
this.setState({
taskName: e.target.value,
})
this.disableButton(e.target.value)
}
disableButton(taskName) {
if(taskName.length == 0) {
this.setState({addNewTaskFieldEmpty: true})
} else {
this.setState({addNewTaskFieldEmpty: false})
}
}
render() {
return (
<div>
<div className="mdc-text-field half-size">
<input className="mdc-text-field__input " onChange={this.onChangeHandler} />
<div className="mdc-line-ripple"></div>
<label className="mdc-floating-label">Task Name</label>
</div>
<a className={"btn-floating btn-large waves-effect waves-light red " + (this.state.addNewTaskFieldEmpty ? 'disabled' : '')} onClick={this.props.addTask(this.state.taskName)}><i className="material-icons">add</i></a>
</div>
)
}
}
export default AddTask
Lint your App.css for any errors.
I encountered this message. I traced it to a CSS include:
.box-table { border-color:; border: 1px solid #dbdad8; }
The missing value of border-color: caused npm run build to fail.
Interestingly, the same file contained
.submenu-button.submenu-opened:after { background:; }
which caused no problems at all.
After get the comments array from post component and pass it to comments component
the logs start to show the error in the screenshot below
the components are:
import React, { Component } from "react";
import axios from "axios";
import Comments from "../components/comments";
class Article extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
error: "",
comment: ""
};
}
componentDidMount() {
this.getComments();
}
getComments = () => {
const {
match: { params }
} = this.props;
return axios
.get(`/articles/${params.id}/comments`, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
}
})
.then(response => {
return response.json();
})
.then(response => this.setState({ comments: response.comments }))
.catch(error =>
this.setState({
error
})
);
};
render() {
return (
<div>
{this.state.title}
<div>
<h2>Comments</h2>
<Comments
getComments={this.getComments}
/>
</div>
</div>
);
}
}
export default Article;
and Comments component
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;
so, I've tried to pass it by props, and set the state on comments component.
and instead of use just comments.map I've tried to use this.state but show the same error in the logs.
So, someone please would like to clarify this kind of issue?
seems pretty usual issue when working with react.
If an error occurs you do:
.catch(error => this.setState({ error }) );
which makes the chained promise resolve to undefined and that is used as comments in the Comments state. So you have to return an array from the catch:
.catch(error => {
this.setState({ error });
return [];
});
Additionally it woupd make sense to not render the Comments child at all if the parents state contains an error.
The other way is checking whether it’s an array and if so check it’s length and then do .map. You have initialized comments to empty array so we don’t need to check whether it’s an array but to be on safer side if api response receives an object then it will set object to comments so in that case comments.length won’t work so it’s good to check whether it’s an array or not.
Below change would work
<div>
{Array.isArray(comments) && comments.length>0 && comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
The first time the comments component renders there was no response yet so comments were undefined.
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
if (!comments) return <p>No comments Available</p>;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;
I have a large JSON file which has around 5000 entries and when I parse it using fetch(), it doesn't show up in browser.
Here's my code:
import React from 'react';
import './Box.css';
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
fetch('./MovieDatabaseShort.json')
.then(a => a.json())
.then(movieName => this.setState({movieName}));
}
renderMovies() {
const { movieName } = this.state;
return movieName.map(a => {
<h1 key={ a.id } className='heading'>{a.title}</h1>;
});
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
export default Box;
I just want to put all the movies titles.
import React from 'react';
import './Box.css';
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
fetch('https://support.oneskyapp.com/hc/en-us/article_attachments/202761627/example_1.json')
.then(a => a.json())
.then(movieName => this.setState({movieName: movieName.color}));
}
render() {
console.log( this.state );
return <div className="box">{this.state.movieName}</div>;
}
}
export default Box;
EDIT- In second code, I just copied random json file from net and it works fine. I think its's due to size of the json file I have. It's 250k+ lines.
Update- This works. I think problem is due to fetch()
import React from 'react';
import './Box.css';
import a from './MovieDatabaseShort.json'
class Box extends React.Component {
constructor() {
super()
this.state = {movieName: []}
}
componentDidMount() {
this.setState({movieName: a});
}
renderBox() {
const { movieName } = this.state;
return movieName.map(k => {
return <h1 className='heading'>{k.title}</h1>;
})
}
render() {
return (
<div className='box'>{this.renderBox()}</div>
);
}
}
export default Box;`
First of all, there are some places you should change in your code.
You should keep an array property in your state for all movies: movies: []
You should map this state value, then render some JSX.
Use componentDidMount instead of componentWillMount since it will be deprecated in a future release.
Here is the example code:
class Box extends React.Component {
constructor() {
super();
this.state = { movies: [] };
}
componentDidMount() {
fetch("./MovieDatabaseShort.json")
.then(res => res.json())
.then(movies => this.setState({ movies }));
}
renderMovies() {
const { movies } = this.state;
return movies.map(movie => (
<h1 key={movie.title} className="heading">
{movie.title}
</h1>
));
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
If you still don't see anything maybe fetch would the problem here. Then, try this:
class Box extends React.Component {
constructor() {
super();
this.state = { movies: [] };
}
componentDidMount() {
import("./MovieDatabaseShort.json").then(movies =>
this.setState({ movies })
);
}
renderMovies() {
const { movies } = this.state;
return movies.map(movie => (
<h1 key={movie.title} className="heading">
{movie.title}
</h1>
));
}
render() {
return <div className="box">{this.renderMovies()}</div>;
}
}
Again, if nothing is shown up please share you JSON file with us as well as check your console if there is any error.
What it looks like you want to do is to save all movies into an array on your state. That would look more like this:
constructor() {
super()
this.state = {movies: []}
}
componentWillMount() {
fetch('./MovieDatabaseShort.json')
.then(a => a.json())
.then(b => this.setState({movies: b}));
}
Then in your render function you would loop over your movies and display the title:
render() {
const { movies } = this.state;
return (
<div className='box'>
{movies.map(movie => <h1 className='heading'>{movie.title}</h1>)}
</div>
);
}
Another way using hook can be the following. In my case I need to take configuration data from a json file
import _data from '../../json/config.json';
export const Mapa = () => {
const [config, setConfig] = useState(null);
useEffect(()=>{
setConfig(_data );
},[]);
}
I'm building a sidebar menu skeleton using ReactJs and need to understand the way to call a function inside ReactJs render() function.
The code is below:
import React from 'react';
var menuData = require("./data/admin.menu.json");
class SidebarMenu extends React.Component {
constructor(props) {
super(props);
this.state = { expanded: true };
this.buildItem = this.buildItem.bind(this);
};
buildItem(title, ref, icon) {
return (
<div className={"item" + this.props.key}>
<a href={ref}>{title}<i className={"fa " + icon} /></a>
</div>
);
};
render() {
return (
<div>
{
menuData.forEach(function (item) {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu")) {
item.submenu.forEach(function (subitem) {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
});
}
})
}
</div>
);
};
}
export default SidebarMenu;
The given code shows the following error:
Uncaught TypeError: Cannot read property 'buildItem' of undefined
How to properly call a function that will render data inside the ReactJs function ?
The this referenced when you try to call this.buildItem() refers to the anonymous function's context, not your React component.
By using Arrow Functions instead of functions defined using the function keyword inside the render() method, you can use this to reference the React component and its methods as desired.
Alternatively, you can use (function () { ... }).bind(this) to achieve the same result. But this is more tedious and the use of arrow functions is preferred.
Below is one solution, using fat arrow, AKA arrow functions:
import React from 'react';
var menuData = require("./data/admin.menu.json");
class SidebarMenu extends React.Component {
constructor(props)
{
super(props);
this.state = { expanded: true };
this.buildItem = this.buildItem.bind(this);
};
buildItem(title, ref, icon) {
return (
<div className={"item" + this.props.key}>
<a href={ref}>{title}<i className={"fa " + item.icon}/></a>
</div>
);
};
render() {
return (
<div>
{
menuData.forEach(item => {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu"))
{
item.submenu.forEach(subitem => {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
});
}
})
}
</div>
);
};
}
export default SidebarMenu;
Another solution would be:
render() {
return (
<div>
{
menuData.forEach(function (item) {
this.buildItem(item.title, item.ref, item.icon);
if (item.hasOwnProperty("submenu"))
{
item.submenu.forEach(function (subitem) {
this.buildItem(subitem.title, subitem.ref, subitem.icon);
}.bind(this));
}
}.bind(this))
}
</div>
);
};
}
But, IMO, the best solution would be to refactor the code using a component:
import React, {PropTypes, Component} from 'react';
const menuData = require('./data/admin.menu.json');
function MenuItem({key, ref, title, icon, submenu}) {
return (
<div className={`item${key}`}>
<a href={ref}>{title}<i className={`fa ${icon}`}/></a>
if (submenu) {
submenu.map((subitem) => <MenuItem {...subitem} />)
}
</div>
);
}
MenuItem.propTypes = {
key: PropTypes.string,
title: PropTypes.string,
ref: PropTypes.string,
icon: PropTypes.string,
submenu: PropTypes.array,
};
class SidebarMenu extends Component {
constructor(props) {
super(props);
this.state = {
expanded: true,
};
}
render() {
return (
<div>
{
menuData.map((subitem) => <MenuItem {...subitem} />)
}
</div>
);
}
}
export default SidebarMenu;
You can add this line:
render() {
let that = this
return (
and then instead of this.buildItem use that.buildItem or you may need that.buildItem.bind(that)