React not rendering an element when used in map() with {} [duplicate] - javascript

This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 4 years ago.
React doesn't render the element when I use this form of code:
class ListaPratitelja extends React.Component {
constructor(props) {
super(props);
}
render() {
const items = this.props.pratitelji;
const listItems = items.map((name, index) => {
e(Pratitelj, { key: index, name: name });
});
return listItems;
}
}
I've used a debugger to see what's going on, and the e function (which is just React.createElement) returns undefined.
But it works just fine when I use it like this:
class ListaPratitelja extends React.Component {
constructor(props) {
super(props);
}
render() {
const items = this.props.pratitelji;
const listItems = items.map((name, index) => e(Pratitelj, { key: index, name: name }));
return listItems;
}
}
The question is why? Is this a bug or I did something wrong in the first example. Also this is the full code:
'use strict';
const e = React.createElement;
class ListaPratitelja extends React.Component {
constructor(props) {
super(props);
}
render() {
const items = this.props.pratitelji;
const listItems = items.map((name, index) => e(Pratitelj, { key: index, name: name }));
return listItems;
}
}
class Pratitelj extends React.Component {
constructor(props) {
super(props);
this.state = { deleted: false };
this.handleDeleteChange = this.handleDeleteChange.bind(this);
}
handleDeleteChange(deletedState) {
this.setState({ deleted: deletedState });
}
render() {
console.log("rendered");
if (this.state.deleted) {
return '';
}
return e(
'div',
null,
e(PratiteljIme, { name: this.props.name }),
e(PratiteljDeleteButton, { handleDeleteChange: this.handleDeleteChange})
);
}
}
class PratiteljIme extends React.Component {
constructor(props) {
super(props);
}
render() {
return e(
"span",
null,
this.props.name)
}
}
class PratiteljDeleteButton extends React.Component {
constructor(props) {
super(props);
}
render() {
return e(
"button",
{ type: "button", "onClick": this.props.handleDeleteChange},
"X"
)
}
}
function loadListaPratitelja(pratitelji) {
const lista = pratitelji.split(",");
const domContainer = document.querySelector('#listaPratitelja');
ReactDOM.render(e(ListaPratitelja, {pratitelji: lista}), domContainer);
}
The input variable "pratitelji" is just a string with a couple of CSV (for ex. p1,p2,p3,p4).
The versions of react I use are these:
https://unpkg.com/react#16/umd/react.development.js
https://unpkg.com/react-dom#16/umd/react-dom.development.js
The browser I tested it on is the latest version of firefox for development.

with using { and } you are creating a function that doesn't return anything, unless you return something. Hence .map will return an array filled with undefined.

Update your code to this:
const listItems = items.map((name, index) => {
return e(Pratitelj, { key: index, name: name });
});
If you open up a function after the arrow, you must state what you're returning.

Related

Dynamically update value based on State in React

I have a react class based component where I have defined a state as follows:
class MyReactClass extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDataPoints: new Set()
};
}
// This method is called dynamically when there is new addition of data
storeData = (metricName, dataPoint) => {
if (this.state.selectedDataPoints.has(dataPoint)) {
this.state.selectedDataPoints.delete(dataPoint);
} else {
this.state.selectedDataPoints.add(dataPoint);
}
};
render () {
return (
<p>{this.state.selectedDataPoints}</p>
);
}
}
Note that initially, the state is an empty set, nothing is displayed.
But when the state gets populated eventually, I am facing trouble in spinning up the variable again. It is always taking as the original state which is an empty set.
If you want the component to re-render, you have to call this.setState () - function.
You can use componentshouldUpdate method to let your state reflect and should set the state using this.state({}) method.
Use this code to set state for a set:
export default class Checklist extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDataPoints: new Set()
}
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
}
addItem(item) {
this.setState(({ selectedDataPoints }) => ({
selectedDataPoints: new Set(selectedDataPoints).add(item)
}));
}
removeItem(item) {
this.setState(({ selectedDataPoints }) => {
const newSelectedDataPoints = new Set(selectedDataPoints);
newSelectedDataPoints.delete(item);
return {
selectedDataPoints: newSelectedDataPoints
};
});
}
getItemCheckedStatus(item) {
return this.state.checkedItems.has(item);
}
// This method is called dynamically when there is new addition of data
storeData = (metricName, dataPoint) => {
if (this.state.selectedDataPoints.has(dataPoint)) {
this.state.selectedDataPoints.removeItem(dataPoint);
} else {
this.state.selectedDataPoints.addItem(dataPoint);
}
};
render () {
return (
<p>{this.state.selectedDataPoints}</p>
);
}
}

Local JSON file is not parsing in React

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 );
},[]);
}

How to inject functions that change component state?

I want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.
So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?
Example function code in Tester.js:
const tester = () => {
return 'new data';
}
export default tester;
Example component code in App.js (without imports):
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
let newData = tester();
this.setState({ data: newData })
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
You could bind your tester function like this (this approach doesn't work with arrow functions):
function tester() {
this.setState({ data: 'new Data' });
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
this.tester = tester.bind(this);
}
componentDidMount() {
this.tester();
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):
function tester(prevState, props) {
return {
...prevState,
data: 'new Data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
}
componentDidMount() {
this.setState(tester);
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
You can pass a function to setState() that will return a new object representing the new state of your component. So you could do this:
const tester = (previousState, props) => {
return {
...previousState,
data: 'new data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
this.setState(tester)
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
The reason being that you now have access to your component's previous state and props in your tester function.
If you just need access to unchanging static placeholder values inside of your app, for example Lorem Ipsum or something else, then just export your data as a JSON object and use it like that:
// testData.js
export const testData = {
foo: "bar",
baz: 7,
};
...
// In your app.jsx file
import testData from "./testData.js";
const qux = testData.foo; // "bar"
etc.

Re-render component with componentWillReceiveProps?

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

Reset initial state in React + ES6

I have a class, ElementBuilder below, and when the user saves the Element they've built, I want the state to reset to the values below.
I have some functions in this class that I haven't provided but that change the state of title, size, and color.
In ES 5, I would have a getInitialState function on my class and could call this.getInitialState() in a function.
This element lives in my app for the lifecycle of a logged in user and I want the default values to always be the same regardless of past usage.
How do I achieve this without writing a function that sets an object of default values (or maybe that's the answer)? thanks!
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = {
title: 'Testing,
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState({ this.getInitialState() });
}
}
You may use a getter function:
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = this.initialState;
}
get initialState() {
return {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState(this.initialState);
}
}
or just a variable:
constructor(props) {
super(props);
this.initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
this.state = this.initialState;
}
Using the proposed class fields, you could do something like this:
class ElementBuilder extends Component {
static initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce'
}
constructor(props) {
super(props)
this.state = ElementBuilder.initialState
}
resetBuilder() {
this.setState(ElementBuilder.initialState)
}
}
Since the initial state doesn't seem to depend on anything instance specific, just define the value outside the class:
const initialState = {...};
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = initialState;
}
resetBuilder() {
this.setState(initialState);
}
}
Use an High Order Component to clear component state (rerender)
Exemple Element.jsx :
// Target ----- //
class Element extends Component {
constructor(props){
super(props)
const {
initState = {}
} = props
this.state = {initState}
}
render() {
return (
<div className="element-x">
{...}
</div>
)
}
}
// Target Manager ----- //
class ElementMgr extends Component {
constructor(props){
super(props)
const {
hash = 0
} = props
this.state = {
hash, // hash is a post.id
load: false
}
}
render() {
const {load} = this.state
if (load) {
return (<div className="element-x"/>)
} else {
return (<Element {...this.props}/>)
}
}
componentWillReceiveProps(nextProps) {
const {hash = 0} = nextProps
if (hash !== this.state.hash) {
this.setState({load:true})
setTimeout(() => this.setState({
hash,
load:false
}),0)
}
}
}
export default ElementMgr

Categories