React state change - reactjs

I am having difficulty in changing state. I would like to change the state with images in an array that has a corresponding description to the images in the array. On click on the chapter should bring up the image and the description. Could you tell me what I am doing wrong here? Ignore chapters 3-6 as I haven't populated that code.
import React from "react";
import {Component} from "react";
import Layout from "../components/layout";
import styles from "./book1.module.css";
import image1 from "../images/rainbow.jpg"
import image2 from "../images/rainbow2.jpg"
class book extends Component {
constructor() {
super();
this.state= {
index : 0,
images : [image1, image2],
ChapterDescriptions: ["chapter1","chapter2"]
}
}
chapter1=()=>{
this.setState({
index: this.state.images.index[0]
})
}
chapter2=()=>{
this.setState({
index: this.state.chapterImages[1]
})
}
chapter3=()=>{
this.setState({
index: this.state.chapter3.image
})
}
chapter4=()=>{
this.setState({
index: this.state.chapter4.image
})
}
chapter5=()=>{
this.setState({
index: this.state.chapter5.image
})
}
chapter6=()=>{
this.setState({
index: this.state.chapter6.image
})
}
render(){
return (
<Layout>
<div className={styles.container}>
<div className={styles.chapters}>
<h1>Anti-Children</h1>
<p>
Learn how our leaders subvert our children’s future and what you can
do to brighten it
</p>
<ul>
<br/>
<br/>
<li className={styles.description} onClick={this.chapter1} > aaaaaaa</li>
<li className={styles.description} onClick={this.chapter2}> bbbbbbbbb</li>
<li className={styles.description} onClick={this.chapter3}> ccccccccc</li>
<li className={styles.description} onClick={this.chapter4}> ddddddddd</li>
<li className={styles.description} onClick={this.chapter5}> eeeeeeeee</li>
<li className={styles.description} onClick={this.chapter6}> fffffffff</li>
</ul>
</div>
<div className={styles.bookimage}>
<img href="#" className={styles.chapterImage} src={this.state.images.index}/>
<div className={styles.textArea} > {this.state.ChapterDescriptions.index}</div>
</div>
</div>
</Layout>
)
}
}
export default book

I've edited and added some parts of the code.
1. You can render some looped tag using map()
{this.state.ChapterDescriptions.map((item,index)=>{
return <li className={style.description} onClick={()=>{handleIndex(index)}>{item}</li>
})
}
2. You can combine your click event for update the state
handleIndex = (index) => {
this.setState({ index: index });
}
3. etc.
<div className={styles.bookimage}>
<img href="#" className={styles.chapterImage} src={this.state.images[this.state.index]}/>
<div className={styles.textArea} > {this.state.ChapterDescriptions[this.state.index]}</div>
</div>
Full code
import React from "react";
import {Component} from "react";
import Layout from "../components/layout";
import styles from "./book1.module.css";
import image1 from "../images/rainbow.jpg"
import image2 from "../images/rainbow2.jpg"
class book extends Component {
constructor() {
super();
this.state= {
index : 0,
images : [image1, image2],
ChapterDescriptions: ["chapter1","chapter2"]
}
}
handleIndex = (index) => {
this.setState({ index: index });
}
render(){
return (
<Layout>
<div className={styles.container}>
<div className={styles.chapters}>
<h1>Anti-Children</h1>
<p>
Learn how our leaders subvert our children’s future and what you can
do to brighten it
</p>
<ul>
<br/>
<br/>
{
this.state.ChapterDescriptions.map((item,index)=>{
return <li className={style.description} onClick={()=>{handleIndex(index)}>{item}</li>
})
}
</ul>
</div>
<div className={styles.bookimage}>
<img href="#" className={styles.chapterImage} src={this.state.images[this.state.index]}/>
<div className={styles.textArea} > {this.state.ChapterDescriptions[this.state.index]}</div>
</div>
</div>
</Layout>
)
}
}
export default book

Please check this code with live example demo. if you want like this you can iterate through map instead of writing hard coded.
let data = [{id:1,chapter: "chapter 1", image:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZlA55Pm9_SZbN90fmVOYgldndkgqSv6k88kQMzvDh36i_no7J&s'},
{id:2,chapter: "chapter 2", image:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQLZZPW62TuM_Qgtu8lMZJ1U9to8BM2A4dEmC27ZyDpzWq9ZAxe&s'},
{id:3,chapter: "chapter 3", image:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRFnKI9xni86qgmhlkHTN6utr__NX_vtgzpQSVBsnKM0Zzb92pG4g&s'},
{id:4,chapter: "chapter 4", image:'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Node.js_logo.svg/1200px-Node.js_logo.svg.png'},
{id:5,chapter: "chapter 5", image:'https://cdn2.iconfinder.com/data/icons/nodejs-1/512/nodejs-512.png'}]
class Book extends Component{
state={selected:''}
clickHandler=list=>{
this.setState({selected:list})
}
render(){
const {selected} =this.state;
return (
<div>
{data.map((d)=>
<p key={d.id} onClick={()=>this.clickHandler(d)}>
{d.chapter}
</p>
)}
<hr />
<h2>Please select an item from above list</h2>
{selected!==''&&<div>
<h3>{selected.chapter}</h3>
<img src={selected.image} alt="chapterimg"
style={{height:100,width:150}}/>
</div>}
</div>
)
}
}
check this for your reference Live demo

First, writing redundant <li> is not a good idea, you can change this by looping through a map function.
{
this.state.ChapterDescriptions.map((item,index)=>(
<li className={style.description} onClick={()=>this.setState({in
dex})}>
{item}
</li>)
}
and for showing the corresponding image
{
<div className={styles.bookimage}>
<img href="#" className={styles.chapterImage} src={this.state.images[this.state.index]}/>
<div className={styles.textArea} > {this.state.ChapterDescriptions[this.state.index]}</div>
</div>
}

Related

Undefined when Filtering an Array in React JS

I am trying to filter an array in React for only certain items (in my case I want all items that have type: "Plant"). I can get the data from an API successfully but filtering its produces this error: Unhandled Rejection (TypeError): undefined is not an object (evaluating 'data.Array.object')
Here is what the data looks like if I console log it:
data
Here is my full code:
import React from "react";
import { API, graphqlOperation } from 'aws-amplify';
import { listTrackerItems } from '../graphql/queries';
class TrackerPlantsPage extends React.Component {
state = {
plant:'',
plants: [],
active: []
};
async componentDidMount() {
const result = await API.graphql(graphqlOperation(listTrackerItems))
let data = result.data.listTrackerItems.items
console.log(data)
let activePlants = data.Array.filter(t=>t.type === 'Plant');
this.setState({active: activePlants, plants: data });
}
render() {
const { plants, active } = this.state
console.log(plants)
return (
<>
<div class="container">
{/* Cards */}
<div id="toggle-harvested" class="">
<div class="uk-child-width-1-3 uk-grid-small uk-grid-match uk-grid">
{plants.map(item => (
<div key={item.id} class="uk-margin-top uk-margin-bottom">
<div class="uk-card uk-card-secondary uk-card-body">
<div class="uk-card-badge uk-label">Harvested on {item.harvestDate}</div>
<div class="uk-child-width-expand#s uk-text-center" uk-grid>
<div>
<div>
<h3 class="uk-card-title uk-margin-remove-bottom">{item.name}</h3>
</div>
</div>
</div>
<ul class="uk-list uk-list-striped">
<li>Planted: {item.todaysDate}</li>
<li>Assigned: {item.assignedUser}</li>
<li>{item.description}</li>
</ul>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</>
)
}
}
export default TrackerPlantsPage;
Regards
do data.filter(t => t.type === 'Plant') instead.

How to print list output dynamically in React JS as buttons

Below is the code snippet of my ReactJS component, I'm trying to put the output with a button beside it in a list format. The output which is received in the "axios" get function is from an api and it is in this format: [{'name': 'i-cc608f4d'}, {'name': 'i-fd608f7c'}, {'name': 'i-fe608f7f'}]
I want to list these IDs with a button besides it.
import React, { Component } from "react";
import axios from "axios";
const pStyle = {
backgroundColor: "#79D3EF",
color: "white"
};
export default class CityContent extends Component {
constructor(props) {
super(props);
this.state = {
citydetail: []
};
}
componentDidMount() {
axios.get("http://127.0.0.1:8000/Delhi").then(res => {
const citydetail = res.data;
this.setState({ citydetail });
});
}
render() {
return (
<div className="content-wrapper">
<section className="content-header">
<div className="row">
<div className="col-md-4">
<div className="box">
<div className="box-body">
<div className="row">
<div className="col-md-8">
<ul>{this.state.citydetail.state}</ul>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
);
}
What should I write to replace {this.state.citydetail.state}.
Because currently, it's printing the API output itself.
<div className="col-md-8">
<ul>
{
this.state.citydetail.map((city) => (
<li>{city.name}<button>Click</button></li>
)}
</ul>
</div>
This code will print your output and map will map through that data of citydetail and button beside it.
It should be something like this
<ul>
{
this.state.citydetail.map((city, index) => (
<li key={index}>{city.name}<button>{city.name}</button></li>
)}
</ul>
Update
Set your state like this in componentDidMount()
this.setState({ citydetail : citydetail });
Update 2 (after OP comment)
I cannot change the format of this output...
try this
axios.get("http://127.0.0.1:8000/Delhi").then(res => {
const citydetail = res.data;
let arrData = JSON.parse(citydetail.replace(/'/g, '"'));
this.setState({ citydetail: arrData });
});

How to call twofunction in componentDidMount?

I failed to call two funtion in componentDidMount.When I clicked bangla its change and when i clicke english its change but during routing it stays only english so i wanted to set the state in componentDidMount,but it only invoke one funtion.if click the bangla it set bangla but when i change the routing its remain the same lang. so how can i set it.
import React, { Component } from 'react';
import { connect } from "react-redux";
import {setLanguage} from "../../actions";
import { Link } from "react-router-dom";
class MenuComp extends Component {
constructor(props){
super(props);
this.setLang = this.setLang.bind(this);
this.state= {
"maintitle": {
"titlelist": [
{"title1":"Timetable"},
{"title2":"Ticket Prices"},
{"title3":"About Us"}
]
}
};
}
setLang(lang){
this.props.setLanguage(lang);
this.props.history.push('/menu');
}
changeLanguage = () => {
this.setState({
"maintitle": {
"titlelist": [
{"title1":"সময়সূচী"},
{"title2":"টিকেটর মূল্য"},
{"title3":"আমাদের সম্পর্কে"}
]
}
});
};
changeLang = () => {
this.setState({
"maintitle": {
"titlelist": [
{"title1":"Timetable"},
{"title2":"Ticket Prices"},
{"title3":"About Us"}
]
}
});
};
componentDidMount() {
this.changeLanguage();
this.changeLang();
}
render() {
return (
<div className="Menu">
<div className="menu-header">
<div className="container-fluid p-0">
<div className="row m-0">
<div className="col-md-4 p-0 d-flex justify-content-end">
<div className="align-self-center">
<a className="lang" onClick={() => { this.setLang('bn'); this.changeLanguage(); }}>Bangla</a> |
<a className="lang l-active" onClick={() => { this.setLang('en'); this.changeLang(); }}>English</a>
</div>
</div>
</div>
</div>
</div>
<main className="navigation">
<div className="container-fluid p-0">
<div className="row m-0">
<div className="col-md-4 pl-0">
<Link to="/timetable" className="lang">
<div className="card-content">
<h6 className="card-title">{
this.state.maintitle.titlelist.map((title, i)=>{
return (<p key={i}>{title.title1} </p>)
})
}</h6>
</div>
</Link>
</div>
<div className="col-md-4 pl-0">
<Link to="/ticketprice" className="lang">
<div className="card-content">
<h6 className="card-title">{
this.state.maintitle.titlelist.map((title, i)=>{
return (<p key={i}>{title.title2} </p>)
})
}</h6>
</div>
</Link>
</div>
</Link>
</div>
</div>
</div>
</main>
</div>
);
}
}
function mapStateToProps(state){
return {
lang: state.lang.lang
}
}
const Menu = connect(mapStateToProps, {setLanguage})(withRouter(MenuComp));
export default Menu;
It's an asynchronous problem. So, the setState method runs asynchronously. This makes reading this.state right after calling setState() a potential pitfall.
So, the lines inside your componentDidMount method get executed, however, you can't predict which one of them will finish before the other.
Now, I don't completely understand what you're trying to achieve, but instead, use componentDidUpdate or a setState callback (setState(updater, callback)): something like this:
this.setState((state, props)=> ({
"maintitle": {
"titlelist": [
{"title1":"সময়সূচী"},
{"title2":"টিকেটর মূল্য"},
{"title3":"আমাদের সম্পর্কে"}
]
}
}), ()=> {// do what you want next!})
// (this could be inside your componentDidMount!
If that didn't help, please let me know!

Adding a CSS class to a JSX in ReactJS

I wanted to add an 'active' class to a menu element, written in ReactJS. I tried doing it with the conventional JS method, but it failed. A click on any <li> tag, should result is removal of the 'active' class from all the <li>, and retain/ add it only to the one list tag in which the click was triggered.
Note: I know it may seem very naive on my part, but I'm just starting with ReactJS. Please ignore the stupidity.
import React, { Component } from 'react';
class Sidebar extends Component{
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className="mb-3 pl-2 menuTitle active" id="MenuTitle1">
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle2" onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle3" onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
}
}
export default Sidebar;
I saw a similar question here, but that couldn't help me.
Idea is, store the id of clicked item in state variable and put the check with className. If item's id is same as state value then only assign the className active.
Write it like this:
class Sidebar extends Component{
constructor() {
super()
this.state = {
activeItem: 'MenuTitle1'
}
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({
activeItem: id,
})
}
getClassName(id) {
if(id === this.state.activeItem) return 'mb-3 pl-2 menuTitle active'
return 'mb-3 pl-2 menuTitle'
}
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
id="MenuTitle1"
className={this.getClassName('MenuTitle1')}
onClick={this.clickMenu.bind(this,'MenuTitle1')}>
...
</li>
<li
id="MenuTitle2"
className={this.getClassName('MenuTitle2')}
onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li
id="MenuTitle3"
className={this.getClassName('MenuTitle3')}
onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
}
You can maintain the state for clicked menu item:
clickMenu(id){
this.setState({activeMenu: id})
}
Then, define className like this:
className={
this.state.activeMenu == id {/* eg. "MenuTitle1" */}
? 'mb-3 pl-2 menuTitle active'
: 'mb-3 pl-2 menuTitle'
}
Like Bhojendra suggested store datas linked to your display inside your state then when you want to update the display of your component use the method setState, this will trigger render again (react style).
import React, { Component } from 'react';
import ReactDOM from "react-dom";
class Sidebar extends Component {
constructor() {
super();
this.state = {
activeMenuId: "MenuTitle1"
};
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle1" ? "active" : ""}`} id="MenuTitle1" onClick={this.clickMenu.bind(this, 'MenuTitle1')}>
1
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle2" ? "active" : ""}`} id="MenuTitle2" onClick={this.clickMenu.bind(this, 'MenuTitle2')}>
2
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle3" ? "active" : ""}`} id="MenuTitle3" onClick={this.clickMenu.bind(this, 'MenuTitle3')}>
3
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({activeMenuId: id});
}
}
export default Sidebar;
ReactDOM.render(<Sidebar />, document.body);
Another way of just make using initialstate and setState.
import React, { Component } from "react";
class Sidebar extends Component {
constructor(props) {
super(props);
this.initialState = {
MenuTitle1: "active",
MenuTitle2: "",
MenuTitle3: ""
};
this.state = this.initialState;
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle1} `}
id="MenuTitle1"
onClick={this.clickMenu.bind(this, "MenuTitle1")}
>
one
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle2}`}
id="MenuTitle2"
onClick={this.clickMenu.bind(this, "MenuTitle2")}
>
two
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle3}`}
id="MenuTitle3"
onClick={this.clickMenu.bind(this, "MenuTitle3")}
>
three
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
this.setState(this.initialState);
this.setState({
[id]: "active"
});
}
}
export default Sidebar;

ReactJS OnClick render

I am new to ReactJS and I am trying to make a menu that by pressing any of the functions the active class will disappear and the new page will appear. In here for instance, I am trying to click on My Order and get forwarded to the page I asked to load. How do I make this correctly? Here is my current code:
import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";
class MainPanel extends React.Component {
render() {
return (
<div className="main-layout">
<header>
<ul className="top">
<h1>Header</h1>
</ul>
</header>
<ul className="categories">
<li>Main Panel</li>
<li onClick={<MyOrder />}>My Order</li>
<li>Technical Support</li>
<li>My Payments</li>
<li>Suggestions/ Questions</li>
</ul>
</div>
);
}
}
function MyOrder () {
return (
<div className="main-layout">
<header>
<ul className="top">
<h1>My Order</h1>
</ul>
</header>
<ul className="categories">
<li>Where is my order?</li>
<li>My order delays more than the expected time</li>
<li>My order status shows that the order arrived but it did not</li>
<li>I have a complaint</li>
<li>Suggestions/ Questions</li>
</ul>
</div>
);
}
ReactDOM.render(
<MainPanel />,
document.getElementById('root')
);
Without React Router and other 3rd party libraries you could do something like this:
class Overview extends React.Component {
onMenuClick(id) {
return this.props.onMenuClick(id);
}
render() {
return(
<ul className="categories">
<li onClick={this.onMenuClick.bind(this, "mainPanel")}>Main Panel</li>
<li onClick={this.onMenuClick.bind(this, "myOrder")}>My Order</li>
<li onClick={this.onMenuClick.bind(this, "technicalSupport")}>Technical Support</li>
<li onClick={this.onMenuClick.bind(this, "myPayments")}>My Payments</li>
<li onClick={this.onMenuClick.bind(this, "suggestions")}>Suggestions/ Questions</li>
</ul>
);
}
}
class MainPanel extends React.Component {
// sets the initial value for this.state.page
componentWillMount() {
this.setState({
page: "overview"
});
}
switchPage(id) {
this.setState({ page: id });
}
showComponent() {
if(this.state.page === "overview") return (<Overview
onMenuClick={::this.switchPage}
/>);
if(this.state.page === "myOrder") return <MyOrder />;
throw new Error(`${this.state.page} is not a valid page id`);
}
render() {
return (
<div className="main-layout">
<header>
<ul className="top">
<h1>Header</h1>
</ul>
</header>
{ this.showComponent() }
</div>
);
}
}
You change the view by updating the state. Depending on the state different components are mounted/unmounted.
Note based on my experience: Try to handle your app state (in our case the current page information) via React Baobab or something like this (centralized state) because otherwise all these props bubblings become confusing.

Resources