when click on button, id is always the same - reactjs

I am using the "react-responsive-modal" npm package
Source code is here
If you clone and run 'npm start'
You will see dummy UI like this
header
click me objId: 43
click me objId: 42
click me objId: 41
When I click 43, 42, 41, the console.log always output 41. I expecting it output 43, 42, 41 individually.
Is it something to do with the onclick? Does it need closure with onclick?
Here is the main code
import React, { Component } from 'react';
//import logo from './logo.svg';
//import './App.css';
import Modal from 'react-responsive-modal';
class App extends Component {
constructor(props) {
super(props);
this.state = {
isModelOpen: false
}
}
mydata() {
let arr = [
{
id: 43,
date: "Nov 26, 2018",
},
{
id: 42,
date: "Nov 26, 2018",
},
{
id: 41,
date: "Nov 26, 2018",
},
];
return arr;
}
modalNoButton() {
this.setState({ isModelOpen: false });
}
modalYesButton(objId, date) {
// test
console.log('-- modalYesButton --');
console.log(objId);
console.log(date);
this.setState({isModelOpen: false});
}
onOpenModal() {
this.setState({ isModelOpen: true });
}
onCloseModal() {
this.setState({ isModelOpen: false });
}
createActionHtml(objId, date) {
// test
//console.log('-- createActionHtml --');
//console.log(objId);
let {isModelOpen} = this.state;
let pointerStyle = {
cursor: 'pointer'
};
let bigMarginStyle = {
marginTop: '30px'
}
return (
<div>
<div className='myPointer'>
<a onClick={this.onOpenModal.bind(this)} style={pointerStyle}>click me objId: {objId}</a>
</div>
<Modal open={isModelOpen} onClose={this.onCloseModal.bind(this)}>
<div style={bigMarginStyle}>
popup
</div>
<div className='row'>
<div className='col xs-6'>
<button
onClick={() => {this.modalYesButton(objId, date)}}
>
Yes
</button>
</div>
<div className='col xs-6'>
<button
onClick={this.modalNoButton.bind(this)}
>
No
</button>
</div>
</div>
</Modal>
</div>
);
}
myhistory() {
let arr = this.mydata();
let html = arr.slice(0, 3).map((obj, index) => {
let objId = obj.id;
let date = obj.date;
let actionHtml = this.createActionHtml(objId, date);
return (
<div key={index}>
{actionHtml}
</div>
);
});
return html;
}
render() {
return (
<div className="App">
<header className="App-header">
header
</header>
{this.myhistory()}
</div>
);
}
}
export default App;

The issue is that you're actually rendering three modals on top of one another, one for each item, and the last modal with id 41 is on top. The state isOpenModal is a single boolean that determines if all three modals render or not, but you really want to track which of the three modals is open, so I would replace isOpenModal with openModalId.
Here is a codesandbox that works: https://codesandbox.io/s/j388zzyow3
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Modal from "react-responsive-modal";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
openModalId: null
};
}
mydata() {
let arr = [
{
id: 43,
date: "Nov 26, 2018"
},
{
id: 42,
date: "Nov 26, 2018"
},
{
id: 41,
date: "Nov 26, 2018"
}
];
return arr;
}
modalNoButton() {
this.setState({ openModalId: null });
}
modalYesButton(objId, date) {
// test
console.log("-- modalYesButton --", objId);
this.setState({ openModalId: null });
}
onOpenModal(objId) {
this.setState({ openModalId: objId });
}
onCloseModal() {
this.setState({ openModalId: null });
}
createActionHtml(objId, date) {
// test
// console.log('-- createActionHtml --', objId);
let { isModelOpen } = this.state;
let pointerStyle = {
cursor: "pointer"
};
let bigMarginStyle = {
marginTop: "30px"
};
return (
<div>
<div className="myPointer">
<a onClick={() => this.onOpenModal(objId)} style={pointerStyle}>
click me objId: {objId}
</a>
</div>
<Modal
open={this.state.openModalId === objId}
onClose={this.onCloseModal.bind(this)}
>
<div style={bigMarginStyle}>popup</div>
<div className="row">
<div className="col xs-6">
<button
onClick={() => {
this.modalYesButton(objId, date);
}}
>
Yes
</button>
</div>
<div className="col xs-6">
<button onClick={this.modalNoButton.bind(this)}>No</button>
</div>
</div>
</Modal>
</div>
);
}
myhistory() {
let arr = this.mydata();
let html = arr.slice(0, 3).map((obj, index) => {
let objId = obj.id;
let date = obj.date;
let actionHtml = this.createActionHtml(objId, date);
return <div key={index}>{actionHtml}</div>;
});
return html;
}
render() {
return (
<div className="App">
<header className="App-header">header</header>
{this.myhistory()}
</div>
);
}
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Related

Toggle class only on one element, react js

I`m changing class after clicking and it works.
The problem is that, classes change simultaneously in both elements and not in each one separately. Maybe someone could look what I'm doing wrong. Any help will be useful.
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
this.setState({
isExpanded: !this.state.isExpanded
});
}
render() {
const { isExpanded } = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>First</strong>
</p>
</div>
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>Second</strong>
</p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
Every element must have its seperate expanded value. So we need an array in state.
And here is the code:
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
state = {
items: [
{ id: 1, name: "First", expanded: false },
{ id: 2, name: "Second", expanded: true },
{ id: 3, name: "Third", expanded: false }
]
};
handleToggle = id => {
const updatedItems = this.state.items.map(item => {
if (item.id === id) {
return {
...item,
expanded: !item.expanded
};
} else {
return item;
}
});
this.setState({
items: updatedItems
});
};
render() {
return this.state.items.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={el.expanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
<span> {el.expanded.toString()}</span>
</p>
</div>
));
}
}
export default PageContentSupportFaq;
You can get two state one state for first and another for a second and handle using two function like this
import React, { Component } from 'react';
class PageContentSupportFaq extends Component {
constructor(props) {
super(props)
this.state = {
isExpanded: false,
isExpanded2:false,
}
}
handleToggle(e){
this.setState({
isExpanded: !this.state.isExpanded
})
}
handleToggle2(e){
this.setState({
isExpanded2: !this.state.isExpanded2
})
}
render() {
const {isExpanded,isExpanded2} = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div onClick={(e) => this.handleToggle(e)} className={isExpanded ? "active" : "dummy-class"}>
<p className="mb-0"><strong>First</strong></p>
</div>
<div onClick={(e) => this.handleToggle2(e)} className={isExpanded2 ? "active" : "dummy-class"}>
<p className="mb-0"><strong>Second</strong></p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
You'll need to track toggled classes in array, that way it will support arbitrary number of components:
// Save elements data into array for easier rendering
const elements = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
expanded: []
};
}
handleToggle(id) {
this.setState(state => {
if (state.isExpanded.includes(id)) {
return state.isExpanded.filter(elId => elId !== id);
}
return [...state.expanded, id];
});
}
render() {
return elements.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={this.isExpanded(el.id) ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
</p>
</div>
));
}
}

how do i toggle disabled between two buttons in react map

I have a list of candidates with two buttons hire and reject. when i press hire it should be disabled and reject stays enabled. When i press reject it should be disabled and hire must be enabled.
{result && result.map(appliedCandidate => {
if (joblist.id === appliedCandidate.jobid) {
return (
<div className="row pb-3">
<div className=" col-md-4 text-left font-weight-bold">
<p className={this.state.applystatus==="hire" ? "text- info" : "text-danger"}>
{appliedCandidate.firstName}
</p>
</div>
<div className="col-md-8">
<div className="row">
<div className="col-4">
<div className="back-btn">
<input id='hire' type='button' ref='hire' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-success card-btn-width" value='hire' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<input id='reject' type='button' ref='reject' data-id={appliedCandidate.jobid} name={appliedCandidate.id} data-tag={appliedCandidate.phoneno} onClick={this.hireReject} className="btn btn-danger card-btn-width" value='reject' />
</div>
</div>
<div className="col-4">
<div className="back-btn">
<Link to={{ pathname: '/individualchat', state: { name: appliedCandidate.firstName, jobid: appliedCandidate.jobid, id: appliedCandidate.id, Title: appliedCandidate.Title } }}>
<button type="button" className="btn btn-info">chat</button>
</Link>
</div>
</div>
</div>
</div>)
}
})}
hireReject = (event) => {
var dis = event.target.setAttribute('disabled','true')
const phoneno = event.target.getAttribute('data-tag');
const id = event.target.getAttribute('name');
const jobid = event.target.getAttribute('data-id');
const applystatus = event.target.value;
{ applystatus === 'hire' ? toastr.success('Successfully hired') : toastr.error('Successfully rejected') }
{ applystatus === 'hire' ? document.getElementById('reject').disabled = false : document.getElementById('hire').disabled = false }
this.setState({
jobid: jobid, id: id, candidatephoneno: phoneno, applystatus: applystatus
}, () => {
this.props.hireReject(this.state)
})
{return applystatus === 'hire' ? 'hired' : 'rejected'}
}
Consider separating the buttons and hiring/rejecting logic into its own component like the following so you can better handle the toggling.
Index.js
import React from "react";
import ReactDOM from "react-dom";
import Candidate from "./Candidate";
import "./styles.css";
class App extends React.Component {
state = {
text: ""
};
render() {
const candidates = [{ name: "Bob" }, { name: "Sam" }, { name: "Jessie" }];
return candidates.map(candidate => {
return <Candidate candidate={candidate} />;
});
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Candidate.js
import React from "react";
class Candidate extends React.Component {
state = {
hired: null
};
handleHire = () => {
this.setState({
hired: true
});
};
handleReject = () => {
this.setState({
hired: false
});
};
render() {
const hired = this.state.hired;
return (
<div>
<h4>{this.props.candidate.name}</h4>
<button
onClick={this.handleHire}
disabled={hired == null ? false : hired}
>
Hire
</button>
<button
onClick={this.handleReject}
disabled={hired == null ? false : !hired}
>
Reject
</button>
</div>
);
}
}
export default Candidate;
Here is a sandbox for your reference as well: https://codesandbox.io/s/zrlyq0l29m

Not all touch-events are fired in array.map react

I want to add events listeners onTouchStart, Move and End to img tag in array.map function, as a result its catch only one event listener(onTouchStart), but if I set this listeners to div with class="header-added-heroes" all 3 listeners work, I read about binding 'this' to array.map and its catch only onTouchStart, I would be grateful for any information on this question.
{this.props.addedHeroes.map( function(el) {
return (<a name={el.link} key={uniqueId()} className="heroes__link">
<div className="hero"> {console.log(' map this : ', this === that)}
{
<img className="hero__image"
onTouchStart={this.onTouchStart}
onTouchMove={this.handleMove}
onTouchEnd={this.onTouchEnd}
src={el.image}
/>
}
</div>
</a>);
}, this )}
full code:
import React from "react";
import uniqueId from "lodash/uniqueId";
import HeroCounter from "../../images/HeroCounter.svg";
class HeaderAddedHeroes extends React.Component {
state = {
heroes: [1, 2, 3, 4],
showCloseButton: 0
};
constructor(props) {
super(props);
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.handleMove = this.handleMove.bind(this);
}
handleMove() {
console.log('moved');
this.setState({ showCloseButton: 1 })
}
onTouchStart() {
console.log('started');
this.setState({ showCloseButton: 2 })
}
onTouchEnd() {
console.log('ended');
this.setState({ showCloseButton: 3 })
}
render() { var that = this;
return (
<header className="header-added-heroes"> { console.log(' this : ', that)}
<div className="header-added-heroes"
onTouchMove={this.handleMove}
onTouchStart={ this.onTouchStart }
onTouchEnd={this.onTouchEnd}>
{ this.state.showCloseButton }
</div>
<div className="heroes">
{this.props.addedHeroes.map( function(el) {
return (<a name={el.link} key={uniqueId()} className="heroes__link">
<div className="hero"> {console.log(' map this : ', this === that)}
{
<img className="hero__image"
onTouchStart={this.onTouchStart}
onTouchMove={this.handleMove}
onTouchEnd={this.onTouchEnd}
src={el.image}
/>
}
</div>
</a>);
}, this )}
</header>
);
}
}
you can always use arrow function.
Solution was: to remove ‘uniqueId()’
{this.props.addedHeroes.map((el, i) => {
return (<a name={el.link} key={i} className="heroes__link">
<div className="hero">
<img className="hero__image"
onTouchStart={this.onTouchStart}
onTouchMove={this.handleMove}
onTouchEnd={this.onTouchEnd}
src={el.image}
/>
</div>
</a>);
}
)}

How to perform the mutation of dropdowns status in ReactJS?

I want to make a change of state when an item is selected in my dropdown but a bug occurs that does not change without clicking again.
I know there are related questions but my code is a particular case.
The same thing happens to me as this image with my two dropdown.
I change "Mes" but it does not work until you return and select another value. There I just changed to "month 4".
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import format from 'date-fns/format';
import gql from 'graphql-tag';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { Link } from 'react-router-dom';
import { graphql } from 'react-apollo';
import '../../../node_modules/bootstrap/dist/css/bootstrap.css';
import './style.css';
import {
ButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import axios from 'axios';
import TituloSmall, { types } from '../TituloSmall';
const query0 = gql`
query postsDiaComoHoy($dia: Int!, $mes: Int!) {
posts(first: 2, dia: $dia, mes: $mes, categoria: 28) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags {
id
titulo
}
}
count
}
}
`;
const renderTagItem = item => {
const { id, titulo } = item;
return (
<Link key={id} to={`/tags/${id}/`}>
<div className="tag">{titulo}</div>
</Link>
);
};
const removeTagHtml = valor => valor.replace(/(<([^>]+)>)/g, '');
const removerTwitter = valor => valor.replace(/- #\w+/g, '');
let updated = 0;
let dates = format(Date(), 'D');
let month = format(Date(), 'M');
export class DayScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props,
currentIndex: 0,
sortItem1: month,
sortItem2: dates,
cantidadMeses: [
'Enero',
'Febrero',
'Marzo',
'Abril',
'Mayo',
'Junio',
'Julio',
'Agosto',
'Septiembre',
'Octubre',
'Noviembre',
'Diciembre',
],
diasMes: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
],
};
}
state = {
items: [],
};
componentDidUpdate() {
this.getArticles();
updated = 0;
}
getArticles = async () => {
const changeArticles = `
{
posts(first: 3, dia: ${this.state.sortItem2}, mes: ${month}) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
views
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags{
id
titulo
}
}
count
}
}
`;
if (updated) {
try {
const response = await axios.get(
`http://localhost:4000/graphql/?query=${changeArticles}`,
) .then(response => {
this.setState(() => ({
items: response.data.data.posts.rows,
}));
let { data } = this.props;
const { items } = this.state;
data.posts.rows = items;
return response;
});
// Log the response so we can look at it in the console
// Set the data to the state
} catch (error) {
// If there's an error, set the error to the state
// this.setState(() => ({ error }));
console.log(error);
// console.log(this.state.error);
}
}
};
selectIndex = direction => {
const { currentIndex } = this.state;
const {
data: {
posts: { count },
},
} = this.props;
let nexIndex = currentIndex + direction;
nexIndex = nexIndex < 0 ? count - 1 : nexIndex;
nexIndex = nexIndex >= count ? 0 : nexIndex;
this.setState({ currentIndex: nexIndex });
};
monthSelected() {
const { sortItem1, sortItem2, dropdownOpen1 } = this.state;
this.setState({
dropdownOpen1: !dropdownOpen1,
});
if (dropdownOpen1) {
month = sortItem1;
dates = sortItem2;
updated = 1;
}
}
dateSelected() {
const { sortItem1, sortItem2, dropdownOpen2 } = this.state;
this.setState({
dropdownOpen2: !dropdownOpen2,
});
if (dropdownOpen2) {
month = sortItem1;
dates = sortItem2;
updated = 1;
}
}
onDiasChanged=(e)=>{
this.setState({sortItem2:[...e.currentTarget.innerHTML]});
}
onMesChanged=(e)=>{
this.setState({sortItem1:[...e.currentTarget.innerHTML]});
}
render() {
const { data } = this.props;
if (data.loading) {
return <div>Loading...</div>;
}
if (data.error) {
return <div>{data.error.message}</div>;
}
if (data.posts.rows.length <= 0) {
return <div>Nada que mostrar...</div>;
}
const {
data: {
posts: { rows },
},
} = this.props;
this.items = this.props.data.posts.rows;
const {
currentIndex,
sortItem1,
cantidadMeses,
sortItem2,
dropdownOpen1,
dropdownOpen2,
diasMes,
} = this.state;
const item = rows[currentIndex] ? rows[currentIndex] : rows[0];
const html = item.fulltext + item.fulltext2;
const image = `${process.env.REACT_APP_IMG_BASE}${item.imagen_intro ||
item.imagen_banner}`;
data.variables.mes = sortItem1;
data.variables.dia = sortItem2;
return (
<div className="containerDiaComoHoyNoticia">
<div className="box">
<span />
<span />
<div className="mesTexto">{cantidadMeses[sortItem1 - 1]}</div>
<div className="diaTexto">{sortItem2}</div>
</div>
<div>
<div className="textoContainer">
<div className="tituloDiaComoHoyNoticia">
{'BUSCA QUE OCURRIÓ EL DÍA QUE TU QUIERAS EN EL FÚTBOL'}
</div>
<div className="separatorLinea" />
<div className="listaMesDia">
<span className="circuloMesDia">1</span>
<span>Mes</span>
<ButtonDropdown
isOpen={dropdownOpen1}
toggle={() => {
this.monthSelected();
}}>
<DropdownToggle color="white" caret>
{sortItem1}
</DropdownToggle>
<DropdownMenu>
{cantidadMeses.map((items, i) => (
<DropdownItem
dropDownValue="Mes"
dropDownValue="Mes" onClick={this.onMesChanged}>
{i + 1}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
<span className="circuloMesDia">2</span>
<span>Dia</span>
<ButtonDropdown
isOpen={dropdownOpen2}
toggle={() => {
this.dateSelected();
}}>
<DropdownToggle caret>{sortItem2}</DropdownToggle>
<DropdownMenu>
{diasMes.map(i => (
<DropdownItem
dropDownValue="Mes" onClick={this.onDiasChanged}>
{i}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
</div>
</div>
</div>
{rows.map(itemArticulo => (
<div className="listaNoticiasContenido">
<img
alt={itemArticulo.titulo}
src={process.env.REACT_APP_IMG_BASE + itemArticulo.imagen_intro}
className="listaNoticiasImagen"
/>
<div className="rectanguloIconoPlay" />
<div className="contenidoArticulo">
<div className="tituloArticulo"><a href="#" onClick = {this.state.currentIndex = 1}>{itemArticulo.titulo}</a></div>
<div className="descripcionArticulo">
{removeTagHtml(itemArticulo.introtext)}
</div>
<div className="escritor">
<div className="nombreAutor">
<div>
<FontAwesomeIcon icon="user-circle" />
<span className="autorArticulo">
{removerTwitter(itemArticulo.autor) || 'Sin autor'}
</span>
<FontAwesomeIcon icon="eye" />
<span className="vistasTotalesArticulos">
{itemArticulo.views}
</span>
<FontAwesomeIcon icon="calendar" />
<span className="cantidadArticulosEscritos">
{itemArticulo.fecha_dia_hoy}
</span>
</div>
</div>
</div>
</div>
<div className="separadorArticulos" />
</div>
))}
<h2 className="tituloDescripcion">{item.titulo}</h2>
<div className="titlesContainer">
<TituloSmall
iconName="user-circle"
label={item.autor || 'Sin autor'}
/>
<TituloSmall
iconName="calendar"
label={format(item.fecha_dia_hoy, 'DD/MM/YYYY')}
/>
</div>
<div className="imageIntro">
<img className="imageDescription" src={image} alt={item.titulo} />
<div className="esquinaFigura">
<div className="boxWhite">
<span />
<span />
<div className="mesTextoBoxWhite">
{cantidadMeses[sortItem1 - 1]}
</div>
<div className="diaTextoBoxWhite">{sortItem2}</div>
</div>
</div>
</div>
<article dangerouslySetInnerHTML={{ __html: html }} />
<TituloSmall iconName="tags" label="Tags" type={types.BIG} />
{item.tags.map(itemsTags => renderTagItem(itemsTags))}
</div>
);
}
}
DayScreen.propTypes = {
data: PropTypes.shape({
loading: PropTypes.bool.isRequired,
currentSortItem: PropTypes.string.isRequired,
error: PropTypes.shape({ message: PropTypes.string }),
}).isRequired,
};
DayScreen.defaultProps = {};
const queryOptions = {
options: () => ({
variables: {
dia: dates,
mes: month,
},
}),
};
export default graphql(query0, queryOptions)(DayScreen);
I want the change of state to be made without pressing double click and the delay that I showed in the image occurs.
Thanks for your help!
You have directly mutated the state and not setting the state using setState method so your render method will not be called at the time of selection and because of it, you will not getting the change immediately once your render method will be call you will get those changes. so your dropdown implementation should be like.
onMessChanged=(e)=>{
this.setState({sortItem2:e.currentTarget.innerHTML});
}
<DropdownMenu>
{diasMes.map(i => (
<DropdownItem
dropDownValue="Mes"
onClick={this.onMessChanged}>
{i}
</DropdownItem>
))}
</DropdownMenu>
As here you are using the reactstrap so it might be possible that your event target may be different so you have to use currentTarget
the currentTarget refers to the element that the event listener directly attached to while the target still refers to the specific element where we clicked.
First error I see is that you can not set the state directly as this.state.sortItem = e.target.innerHTMLInstead you have to use this.setState({ sortItem: e.targetinnerHTML })
Y por favor, intenta no usar Spanglish xD es una mala práctica.

How to make use of props in a more efficient way?

I have a code in which I receive the values ​​of two dropdown and then I make a request to my graphql by GET method with AXIOS. I would like to know if I can use props to not do that kind of thing? How could I do it?.
There are two dropdown and if I select the day and the month, I should leave the results without reloading the entire page.
Here are some pictures of how this works:
Select day and/or month:
The result and the request with Axios:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import format from 'date-fns/format';
import gql from 'graphql-tag';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { Link } from 'react-router-dom';
import { graphql } from 'react-apollo';
import Divider from '../Divider';
import TituloSmall, { types } from '../TituloSmall';
import Titulo from '../Titulo';
import '../../../node_modules/bootstrap/dist/css/bootstrap.css';
import './style.css';
import {
ButtonDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import { addMonths } from 'date-fns';
import axios from 'axios';
const query0 = gql`
query postsDiaComoHoy($dia: Int!, $mes: Int!) {
posts(first: 2, dia: $dia, mes: $mes, categoria: 28) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags {
id
titulo
}
}
count
}
}
`;
const renderTagItem = item => {
const { id, titulo } = item;
return (
<Link key={id} to={`/tags/${id}/`}>
<div className="tag">{titulo}</div>
</Link>
);
};
const removeTagHtml = valor => valor.replace(/(<([^>]+)>)/g, '');
const removerTwitter = valor => valor.replace(/- #\w+/g, '');
var updated = 0;
var dates = format(Date(), 'D'),
month = format(Date(), 'M');
export class dayScreen extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
sortItem1: month,
sortItem2: dates,
cantidadMeses: [
'Enero',
'Febrero',
'Marzo',
'Abril',
'Mayo',
'Junio',
'Julio',
'Agosto',
'Septiembre',
'Octubre',
'Noviembre',
'Diciembre',
],
diasMes: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
],
};
}
state = {
error: null,
items: [],
};
componentDidUpdate() {
this.getAnime();
this.render();
updated = 0;
}
getAnime = async () => {
const changeArticles = `
{
posts(first: 3, dia: ${dates}, mes: ${month}) {
rows {
id
fecha_dia_hoy
imagen_intro
titulo
introtext
autor
views
fulltext
fulltext2
imagen_banner
categoria {
id
}
tags{
id
titulo
}
}
count
}
}
`;
if (updated) {
try {
const response = await axios.get(
'http://localhost:4000/graphql/?query=' + changeArticles,
);
// Log the response so we can look at it in the console
// Set the data to the state
this.setState(() => ({
isLoaded: true,
items: response.data.data.posts.rows,
}));
this.props.data.posts.rows = this.state.items;
} catch (error) {
// If there's an error, set the error to the state
this.setState(() => ({ error }));
console.log(this.state.error);
}
}
};
selectIndex = direction => {
const { currentIndex } = this.state;
const {
data: {
posts: { count },
},
} = this.props;
let nexIndex = currentIndex + direction;
nexIndex = nexIndex < 0 ? count - 1 : nexIndex;
nexIndex = nexIndex >= count ? 0 : nexIndex;
this.setState({ currentIndex: nexIndex });
};
monthSelected() {
this.setState({
dropdownOpen1: !this.state.dropdownOpen1,
});
if (this.state.dropdownOpen1) {
this.props.data.variables.mes = this.state.sortItem1;
this.props.data.variables.dia = this.state.sortItem2;
month = this.state.sortItem1;
dates = this.state.sortItem2;
updated = 1;
}
}
dateSelected() {
this.setState({
dropdownOpen2: !this.state.dropdownOpen2,
});
if (this.state.dropdownOpen2) {
this.props.data.variables.mes = this.state.sortItem1;
this.props.data.variables.dia = this.state.sortItem2;
month = this.state.sortItem1;
dates = this.state.sortItem2;
updated = 1;
}
}
render() {
const { data } = this.props;
if (data.loading) {
return <div>Loading...</div>;
}
if (data.error) {
return <div>{data.error.message}</div>;
}
if (data.posts.rows.length <= 0) {
return <div>Nada que mostrar...</div>;
}
const {
data: {
posts: { rows },
},
} = this.props;
const { currentIndex } = this.state;
const item = rows[currentIndex];
let html = item.fulltext + item.fulltext2;
const description = item.introtext.replace(/(<([^>]+)>)/gi, '');
const image = `${process.env.REACT_APP_IMG_BASE}${item.imagen_intro ||
item.imagen_banner}`;
return (
<div className="containerDiaComoHoyNoticia">
<div className="box">
<span />
<span />
<div className="mesTexto">
{this.state.cantidadMeses[this.state.sortItem1 - 1]}
</div>
<div className="diaTexto">{this.state.sortItem2}</div>
</div>
<div>
<div className="textoContainer">
<div className="tituloDiaComoHoyNoticia">
{'BUSCA QUE OCURRIÓ EL DÍA QUE TU QUIERAS EN EL FÚTBOL'}
</div>
<div className="separatorLinea"></div>
<div className="listaMesDia">
<span className="circuloMesDia">1</span>
<span>Mes</span>
<ButtonDropdown
isOpen={this.state.dropdownOpen1}
toggle={() => {
this.monthSelected();
}}>
<DropdownToggle color="white" caret>
{this.state.sortItem1}
</DropdownToggle>
<DropdownMenu>
{this.state.cantidadMeses.map((items, i) => (
<DropdownItem
dropDownValue="Mes"
onClick={e => {
this.state.sortItem1 = e.target.innerHTML;
}}>
{i + 1}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
<span className="circuloMesDia">2</span>
<span>Dia</span>
<ButtonDropdown
isOpen={this.state.dropdownOpen2}
toggle={() => {
this.dateSelected();
}}>
<DropdownToggle caret>{this.state.sortItem2}</DropdownToggle>
<DropdownMenu>
{this.state.diasMes.map(i => (
<DropdownItem
dropDownValue="Mes"
onClick={e => {
this.state.sortItem2 = e.target.innerHTML;
}}>
{i}
</DropdownItem>
))}
</DropdownMenu>
</ButtonDropdown>
</div>
</div>
</div>
{rows.map((item, index) => (
<div className="listaNoticiasContenido">
<img
alt={item.titulo}
src={process.env.REACT_APP_IMG_BASE + item.imagen_intro}
className="listaNoticiasImagen"
/>
<div className="rectanguloIconoPlay"></div>
<div className="contenidoArticulo">
<div className="tituloArticulo">{item.titulo}</div>
<div className="descripcionArticulo">
{removeTagHtml(item.introtext)}
</div>
<div className="escritor">
<div className="nombreAutor">
<div>
<FontAwesomeIcon icon="user-circle" />
<span className="autorArticulo">
{removerTwitter(item.autor) || 'Sin autor'}
</span>
<FontAwesomeIcon icon="eye" />
<span className="vistasTotalesArticulos">{item.views}</span>
<FontAwesomeIcon icon="calendar" />
<span className="cantidadArticulosEscritos">
{item.fecha_dia_hoy}
</span>
</div>
</div>
</div>
</div> <div className="separadorArticulos"></div>
</div>
))}
<h2 className="titulo">{item.titulo}</h2>
<div className="titlesContainer">
<TituloSmall
iconName="user-circle"
label={item.autor || 'Sin autor'}
/>
<TituloSmall
iconName="calendar"
label={format(item.fecha_dia_hoy, 'DD/MM/YYYY')}
/>
</div>
<div className="imageIntro">
<img className="imageDescription" src={image} alt={item.titulo} />
<div class="esquinaFigura">
<div className="boxWhite">
<span />
<span />
<div className="mesTextoBoxWhite">
{this.state.cantidadMeses[this.state.sortItem1 - 1]}
</div>
<div className="diaTextoBoxWhite">{this.state.sortItem2}</div>
</div>
</div>
</div>
<article dangerouslySetInnerHTML={{ __html: html }} />
<TituloSmall iconName="tags" label="Tags" type={types.BIG} />
{item.tags.map(item => renderTagItem(item))}
</div>
);
}
}
dayScreen.propTypes = {
data: PropTypes.shape({
loading: PropTypes.bool.isRequired,
error: PropTypes.shape({ message: PropTypes.string }),
}).isRequired,
};
dayScreen.defaultProps = {};
const queryOptions = {
options: () => ({
variables: {
dia: dates,
mes: month,
},
}),
};
export default graphql(query0, queryOptions)(dayScreen);
How could I do this without Axios? When doing it with Axios, is this scalable? I have been told that the props are for that, but I do not know how to do it.
I would like to do this without Axios and only using props. Receive the value of the dropdowns and change that part of the page doing the query with graphql.
You can do this by implementing Apollo Client, start by installing Apollo and then set it up in your project following the documentation.

Resources