Running a Array of Objects outside render function - arrays

Either fails to compile defining variables inside componentDidMount. I did a bunch of dozens of other ways. None seems to work for my particular piece of code. I think reading is better than trying to explain.
import React from 'react';
import { connect } from './api';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
giphy: []
}
}
componentDidMount(){
connect(message => {
this.setState({
giphy: message
})
});
var Items = this.state.giphy.map(function(gif){ // items is not defined.
return <li>{gif}</li>;
})
}
render () {
return (
<div className=".App-logo">
<ul>
{ Items } // I wanted to show all items inside the array of objects.
</ul>
<ul className=".App-logo">
// the following method works. We need to make sure to check for this conditions or wont work
{this.state.giphy && this.state.giphy.length > 0 &&
<img src={ this.state.giphy[2].images.original.url}
alt="giphy.com animations"/>}
</ul>
</div>
)
}
}
If I remove the items, it will show the 2nd item in the state.
Can you help to show all in the state?

Instead of creating a variable in componentDidMount which cannot be used inside of render method, you can directly map your state in render method.
<ul>
//This will show only `bitly_gif_url`
{Array.isArray(this.state.giphy) && this.state.giphy.map(gif => <li>{gif.bitly_gif_url}</li>) }
</ul>
Note: Your giphy array contains number of objects. From each object I have shown only bitly_gif_url using {gif.bitly_gif_url}, if you need to show any other item from your object you can change it's key.
You can show mutltiple item's at a time also,
<ul>
//This will show `bitly_gif_url` and `embed_url` at a time
{Array.isArray(this.state.giphy) && this.state.giphy.map(gif => <li>{gif.bitly_gif_url} {gif.embed_url}</li>) }
</ul>

As you have defined Items inside your componentDidMount function it have a functional scope and will not be available inside render function what you can do is return the items from a function. So now your code will look something like
import React from 'react';
import { connect } from './api';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
giphy: []
}
}
componentDidMount(){
connect(message => {
this.setState({
giphy: message
})
});
}
getItems() {
return this.state.giphy.map(function(gif){
return <li>{gif}</li>;
})
}
render () {
return (
<div className=".App-logo">
<ul>
{ this.getItems() } // I wanted to show all items inside the array of objects.
</ul>
<ul className=".App-logo">
// the following method works. We need to make sure to check for this conditions or wont work
{this.state.giphy && this.state.giphy.length > 0 &&
<img src={ this.state.giphy[2].images.original.url}
alt="giphy.com animations"/>}
</ul>
</div>
)
}
}

Related

React array update

Working on reactJS project, and I know we don't mutate the state (usually i don't but don't know the exact reason) so I just tried some wrong approach even its working correctly.
import logo from './logo.svg';
import './App.css';
import Test from './test';
import {Home, NotFoundComponent,Contact, Profile} from './test/home'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
list: [42, 33, 68],
};
}
onUpdateItem = i => {
this.state.list[i]++;
this.setState({
list: this.state.list,
})
// this.setState(state => {
// const list = state.list.map((item, j) => {
// if (j === i) {
// return item + 1;
// } else {
// return item;
// }
// });
// return {
// list,
// };
// });
};
render() {
return (
<div>
<ul>
{this.state.list.map((item, index) => (
<li key={item}>
The person is {item} years old.
<button
type="button"
onClick={() => this.onUpdateItem(index)}
>
Make me one year older
</button>
</li>
))}
</ul>
</div>
);
}
}
export default App;
this.state.list[i]++;
this.setState({
list: this.state.list,
})
what is the problem with when I update the code above instead map method, both give the correct output?
explain to me this behind the scene
code sandbox link
Here is the reason:
When you mutate an array, say list[i]++ the identity of the array is not changed so listBefore ==== listAfter, while you map over the array listBefore !== listAfter, when you invoke setState, ALL the children component will be invoked again by default even if you put nothing inside setState. Because react can not tell if the children update is necessary by default and it is a waste of resources actually.
You can prevent this from happening by telling the Children "Do NOT update" by calling shouldComponentUpdate or simply use PureComponent.
And the way to tell if updates are necessary is to compare using ===
In addition, Even if you do not care about performance. you should never do this. It will cause some debuggable bugs in some cases mainly because the updates in react is NOT synchronized. I never encounter by the way because I do not even dare to try
When you update your state you should take care of immutable.
onUpdateItem = i => {
//this.state.list[i]++;
//this.setState({
// list: this.state.list,
//})
this.setState({
list: this.state.list.map((value,index)=>{
if(i === index) return value+1;
return value;
})
});
}
If you are not maintaining immutable, your app will not work what you expected.
React states are updated follow life-cycle.
if without immutability, it means you are breaking the life-cycle.

How to print a list of objects from firebase in react?

I am getting the snapshot from the firebase and setting in the setState method and the in the render method i just want to print the list. In the first place because firebase is returning an object the map function was not working and i found something Array.from(this.state.testList) which seems to work but nothing is displayed. I am attaching the code and my firebase tree so you can see what I am trying to do.
25-10-2019:
firstPoint:
newsDesc: "Chris is promoted to scrum master!"
newsTitle: "Chris is promoted!!"
secondPoint:
newsDesc: "Christodoulos needed a change in his life"
newsTitle: "Christodoulos is leaving"
In the code above i am getting the snapshot of 'kabalaNews' which will return me and object with the data and this object will have many other objects with the points and the each point has a title and a description.
import React, { Component } from 'react'
import firebase from 'firebase';
import {DB_CONFIG} from './Config';
export class printFirebase extends Component {
constructor() {
super()
this.app = firebase.initializeApp(DB_CONFIG);
this.database=this.app.database().ref().child('kabalaNews');
this.state = {
testList: []
}
}
componentDidMount(){
this.database.on('value' , snap=> {
this.setState({
testList:snap.val()
});
});
}
render() {
console.log(this.state.testList);
return (
<div>
<div>
{Array.from(this.state.testList).map(news =>
<p>{news.25-10-2019.newsDesc} {news.25-10-2019.newsTitle}
</p>)}
</div>
</div>
)
}
}
export default printFirebase
You have a few mistakes:
I'd actually state the entire snapshot into the state, instead of just the values. This ensures you can meet two needs that you're bound to have in the future:
The items will show up in the order in which you queried them, instead of in alphabetical order of their keys.
You have access to the ID of each item, which is useful once you want to start manipulating the items.
You're using an invalid syntax for accessing properties here: news.25-10-2019.newsDesc. A JavaScript identifier cannot contain a -, so you'll need to use [] notation to access the value: news["25-10-2019"].newsDesc.
So in total that becomes:
componentDidMount(){
this.database.on('value' , snap=> {
this.setState({
testList:snap
});
});
}
render() {
console.log(this.state.testList);
return (
<div>
<div>
{this.state.testList.forEach(news =>
<p>{news.val["25-10-2019"].newsDesc} {news.val["25-10-2019"].newsTitle}
</p>)}
</div>
</div>
)
}
}

Rendering in react with array.map

I have an array of strings which I would like to render as a list, with a colored text. The user can change the color with a button.
For that I have built a component called which receives an array and renders a list with the array's values and a button to change the color:
import React, { Component } from "react";
const renderArray = arr => (arr.map(value => (
<li>
{value}
</li>
)))
class List extends Component {
constructor(props) {
super(props);
this.state = {
color: 'red'
}
}
toggleColor = () => {
if (this.state.color === "red") {
this.setState({color: "blue"});
} else {
this.setState({color: "red"});
}
}
render() {
const style = {
color: this.state.color
};
return (
<div style={style}>
<ul>
{renderArray(this.props.array)}
</ul>
<button onClick={this.toggleColor}>Change color</button>
</div>
);
}
}
export default List;
The List is called with:
<List array={arr} />
And arr:
const arr = ['one', 'two', 'three'];
Fiddle here: Fiddle
But this seems incorrect to me. I rerender the whole array by calling renderArray() each time the color changes. In this case it is not too bad but what if the renderArray() is much more complex?
To my understanding, I need to create a new list only if the array prop changes and this could do in getDerivedStateFromProps (or in componentWillReceiveProps which will be deprecated...):
componentWillReceiveProps(nextProps)
{
const renderedArray = renderArray(nextProps.array);
this.setState({ renderedArray });
}
And then, on render, use this.state.renderedArray to show the list.
But this seems strange, to store a rendered object in the state...
Any suggestions?
Thanks!
1) React uses the concept of virtual DOM to calculate the actual difference in memory and only if it exists, render the difference into DOM
2) You can "help" React by providing a "key", so react will better understand if it's needed to re-render list/item or not
3) Your code componentWillReceiveProps can be considered as a bad practice because you're trying to make a premature optimization. Is repaint slow? Did you measure it?
4) IMHO: renderArray method doesn't make sense and can be inlined into List component
React render the DOM elements efficiently by using a virtual DOM and checks if the update needs to happen or not and hence, it may not be an issue even if you render the list using props. To optimise on it, what you can do is to make use of PureComponent which does a shallow comparison of state and props and doesn't cause a re-render if nothing has changed
import Reactfrom "react";
const renderArray = arr => (arr.map(value => (
<li>
{value}
</li>
)))
class List extends React.PureComponent { // PureComponent
constructor(props) {
super(props);
this.state = {
color: 'red'
}
}
toggleColor = () => {
if (this.state.color === "red") {
this.setState({color: "blue"});
} else {
this.setState({color: "red"});
}
}
render() {
const style = {
color: this.state.color
};
return (
<div style={style}>
<ul>
{renderArray(this.props.array)}
</ul>
<button onClick={this.toggleColor}>Change color</button>
</div>
);
}
}
export default List;

React onChange not working as intended

I'm trying to create a products component that get's all the products available on the website and displays each of them in sort of like a box and when the user clicks that box they get redirected to that product page. I'm using react and redux and I'm having a difficulty with onClick. This is how my code looks
class Products extends Component{
constructor(){
super();
this.state = {
products: [...Some array]
};
}
handleProductRedirect(productNumber){
console.log(productNumber)
// Redux function
// this.props.handleRedirect(productNumber)
}
render(){
var products = this.state.products
return (
<div id="content">
{product &&
<div id="searchContent">
{product.map(element => <Item element={element}
handleProductRedirect={this.handleProductRedirect.bind(this)}
key={element['productNumber']}/>)}
</div>
}
</div>
</div>
)
}
};
class Item extends Component{
render(){
var element = this.props.element;
return (
<div id="itemBox" onClick={this.props.handleProductRedirect(element['productNumber'])}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
)
}
}
so the component gets the products from an api and once it's get them it iterates through them. However, I noticed using chromes developer console that every that it iterates through every <Item /> component it calls handleProductRedirect even though that Item wasn't clicked on. It does it automatically. Instead of calling that function when the div itemBox is clicked on, it calls it when it's rendered. Any suggestions
That's because you are calling the handleProductRedirect on every render for each item. Instead of that, you need send the callback in the onClick prop, something like this:
class Item extends Component{
onClickItem = () => { // <=== Defines the callback and bind it to the instance
const { element } = this.props;
this.props.handleProductRedirect(element['productNumber']);
};
render(){
var element = this.props.element;
return (
<div id="itemBox" onClick={this.onClickItem}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
)
}
}
This way you are not calling the callback on every render, but when the user actually clicks element.
Also, don't forget to define the propTypes on your components, it really helps to catch issues later on.
Your onClick is calling the function here:
onClick={this.props.handleProductRedirect(element['productNumber'])}>
Instead you should return a function that calls it with the argument. You can do that by making an arrow function like this:
onClick={() => this.props.handleProductRedirect(element['productNumber'])}>
But the best way to do it is to extract it into a class method (so that you don't get unnecessary re-renders):
class Item extends Component {
clickProduct = () => {
this.props.handleProductRedirect(this.props.element['productNumber']);
}
render() {
var element = this.props.element;
return (
<div id="itemBox" onClick={this.clickProduct}>
<h3>{elementTitle.slice(0, 85)}</h3>
<p>{element.Manufacturer}</p>
</div>
);
}
}

Can I add two proptypes together after they are rendered? If so, how can I accomplish that?

()I have a div with a prop that I would like to display based on whether a prop is bigger in number than another prop. I have a lot going on in this particular component and I'm concerned that all of the following things I'm trying to do are not possible.
this.props.currentValue < this.props.newValue is not working for me, but everything else is working just fine.
I'm very new to React. Any help would be awesome!
Oh, and the value of currentValue and newValue are inside of the rates component on a separate page.
import React, {PropTypes, Component} from 'react';
import Header from '../compare-table-header/compare-table-header';
import './compare-table-row.css';
export class Rates extends Component {
constructor(props) {
super(props);
this.displayThing = this.displayThing.bind(this);
}
displayThing() {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
render() {
const {currentValue, newValue} = this.props;
return (
<div>
<Header heading="Rates" />
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
</div>
<div>{this.displayThing()}</div>
</div>
</div>
</div>
);
}
}
Rates.propTypes = {
currentValue: PropTypes.number,
newValue: PropTypes.number
};
The line with this.displayThing isn't rendering anything because you're passing a reference to the function itself, instead of calling the function and rendering the value it returns.
That line should do what you expect if you change this.displayThing to this.displayThing().
But you also have some mismatched tags. The Header component is opened and closed on the same line. From your indentation, it looks like you meant for the lines below it to be rendered as children of the Header component, but that's not what's actually happening.
You could clean that up like this:
return (
<div>
<Header heading="Rates">
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
</Header>
<div>{this.displayThing()}</div>
</div>
);
Or, if your Header component doesn't render any children, that might look like this:
return (
<div>
<Header heading="Rates" />
<div className="value-heading">{currentValue}</div>
<div className="value-heading">{newValue}</div>
<div>{this.displayThing()}</div>
</div>
);
If you want to go a little further, you can also remove some code and simplify the class a little by defining the displayThing function as an arrow function:
Instead of this:
export class Rates extends Component {
constructor(props) {
super(props);
this.displayThing = this.displayThing.bind(this);
}
displayThing() {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
// ... rest of the class
}
you can make displayThing into an arrow function and get rid of the constructor, like this:
export class Rates extends Component {
displayThing = () => {
const increase = <div>{this.props.details}</div>;
const thing = <div>hi</div>;
if (this.props.currentValue < this.props.newValue) {
return increase;
} else {
return thing;
}
}
// ... rest of the class
}
The class works the same either way, but it saves a few lines of code.

Resources