Passing arguments to event handlers and getting the value in ReactJS? - reactjs

I have implemented pagination component which has 5 pages in total. Pagination is child component of home component.
When I click on page 1 it should get page number through getpagenumber() and handleClick() which makes use of page number but in below code getpagenumber() does not work when we click on page number.
pagination.js:
import React, { Component } from 'react';
class Pagination extends Component {
getpagenumber(val){
return val;
}
handleClick(){
this.setState({
end:getpagenumber()*16,
start:end-16
});
const end = getpagenumber()*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li {this.getpagenumber(1)} onClick={this.handleClick}>1</li>
<li {this.getpagenumber(2)} onClick={this.handleClick}>2</li>
<li {this.getpagenumber(3)} onClick={this.handleClick}>3</li>
<li {this.getpagenumber(4)} onClick={this.handleClick}>4</li>
<li {this.getpagenumber(5)} onClick={this.handleClick}>5</li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
home.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Home extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16
};
}
onChangePagination = (start, end) => {
this.setState({
start:start,
end:end
});
};
render() {
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Home;

You are exaggerating a bit. All you need is this:
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(value){
this.setState({
end: value*16,
start: end-16
});
const end = value*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<Content/>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
Notes:
Maybe consider even passing the number of pages to the Pagination component and then generating an array from 1 to that number and just mapping out the li with the clicks.
Clicks should go on the link tags.

Related

Incorrect value shown when calling parent function from child Reactjs

I try to call parent function from child component with dynamic value but the value is always showing 80 , Here is the code:
Parent:
filterdata = (child) =>{
alert(child) // this is always showing 80
this.setState({
max:child
})
}
Inside render():(characters is data fetched from axios)
<Pagination pages={Math.ceil(this.state.characters.length/10)} filter={this.filterdata} />
Inside Pagination.js
import React, { Component } from 'react'
class Pagination extends Component {
render() {
var indents = [];
for (var i = 0; i < this.props.pages; i++) {
indents.push( <li key={i} className="page-item"><a class="page-link" onClick={() => this.props.filter((i+1)*10)}>{i+1}</a></li>);
}
return (
<div>
<ul class="pagination justify-content-center">
{indents}
</ul>
</div>
)
}
}
export default Pagination
Like Marc said, I don't think you should do the loop inside render.
import React, { Component } from 'react'
class Pagination extends Component {
constructor(props) {
super(props);
this.state = { indents: [] };
}
componentDidMount() {
vat indents = [];
for (var i = 0; i < this.props.pages; i++) {
indents.push(<li key={i} className="page-item"><a class="page-link" onClick={() => this.props.filter((i+1)*10)}>{i+1}</a></li>);
}
this.setState({ indents: indents });
}
render() {
return (
<div>
<ul className="pagination justify-content-center">
{indents}
</ul>
</div>
)
}
}
export default Pagination
Also, you are using class instead of className on the ul element.
I'd change your component to look like this for the sake of brevity. Like others said though, you could outsource the li generating logic to a method in your class later if the class becomes bigger. This isn't what's causing your problem, though.
I'm not sure what's causing the constant 80, but this code works for me.
in App.js
import Pagination from 'wherever it comes from';
import React from 'react';
class App extends Component {
state = { max: null }
render() {
return (
<div className="App">
<Pagination pages={5} filter={(val) => {
console.log(val);
this.setState({ max:val });
}} />
</div>
)
}
}
Pagination.js
import React, { Component } from 'react';
class Pagination extends Component {
render() {
return (
<div>
<ul className="pagination justify-content-center">
{Array(this.props.pages)
.fill()
.map((el, index) => {
const valueForFilter = (index + 1) * 10;
return (
<li key={index} className="page-item">
<a class="page-link" href="#" onClick={() => this.props.filter(valueForFilter)}>
{index + 1}
</a>
</li>
);
})}
</ul>
</div>
);
}
}
export default Pagination;

passing function from parent to child component

I am trying to do a single page application navigation, so the components load into the page container when the user clicks on menu links. This is what I've tried:
App.tsx:
import * as React from 'react';
import Home from './components/Home';
import MainMenu from './components/MainMenu';
import SignUp from './components/SignUp';
interface IProps {
SignUp?: boolean;
}
interface IState{
SignUp: boolean;
}
class App extends React.Component<IProps,IState>
{
constructor(props: IProps, state: IState) {
super(props);
this.renderPage = this.renderPage.bind(this);
this.state = state;
this.setState({
SignUp: false
});
}
public renderPage(page: string)
{
if(page === 'SignUp')
{
this.setState({
SignUp: true
});
}
}
public render()
{
let page = <Home/>;
if(this.state.SignUp)
{
page = <SignUp/>;
}
return (
<div className="App">
<MainMenu renderPage={this.renderPage}/>
<div className="container-fluid">
{page}
</div>
</div>
);
}
}
export default App;
MainMenu.tsx:
import * as React from 'react';
import './Home.css';
interface IProps
{
renderPage: (page: string) => void
}
class MainMenu extends React.Component<IProps> {
public render() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="#" onClick={this.props.renderPage('Home')}>
Home
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#" onClick={this.props.renderPage('SignUp')}>
Sign Up
</a>
</li>
</ul>
</div>
</nav>
);
}
}
export default MainMenu;
I got this error in MainMenu.tsx:
Type 'void' is not assignable to type '((event:
MouseEvent) => void) | undefined'.
Inside MainMenu component you invoke the passed function, not assingn it to an a element.
<a className="nav-link" href="#" onClick={() => this.props.renderPage('SignUp')}>
Sign Up
</a>
The example above should solve the error, even though you don't pass the event argument.
However, wouldn't it be better if you used react-router, which handles rendering components depending on navigation?

how to use onClick method in another file

I need to filter AssessmentCards by Year. I made the method.
But I need to call clickAllCards and clickYearCard method in onClick event on other file. How can I do that?
This is my code with the methods, I'm using Pug.JS to render:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import messages from './messages';
import { getAssessmentsCards } from '../../functions';
import template from './index.pug';
const cardsAssessments = getAssessmentsCards();
export default class CardAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props){
super(props);
this.state = {
listCards: [],
openCm: false,
}
}
componentWillMount(){
this.setState({listCards: cardsAssessments});
}
hover() {
this.setState({openCm: !this.state.openCm});
}
clickAllCards(e){
e.preventDefault();
this.setState({listCards: cardsAssessments});
}
clickYearCard(e){
e.preventDefault();
var filtered = cardsAssessments.filter((data) => {
return data.yearCard === '2018';
});
this.setState({listCards: filtered});
}
render() {
let cm = ["card-menu"];
if(this.state.openCm) {
cm.push('active');
}
return template.call(this, {
messages,
FormattedMessage,
Link,
cm
});
}
}
This is my pug file:
.card-adjust
div(href="" onClick="{this.clickYearCard.bind(this)}") 2018
div(href="" onClick="{this.clickAllCards.bind(this)}") All
Link.card.add-new(to="/add-assessment")
span
.add-icon
i.ti-plus
|
FormattedMessage(__jsx='{...messages.addAssessment}')
.card.card-materia(#for='data in this.state.listCards', key='{data.id}')
.card-body(id="{data.id}")
div(className="{cm.join(' ')}" onClick="{this.hover.bind(this)}")
i.fas.fa-ellipsis-v
.cm-floating
Link.cmf-agenda(to="/agendamento")
i.ti-agenda
|
FormattedMessage(__jsx='{...messages.scheduled}')
Link.cmf-copy(to="#")
i.pe-7s-copy-file
|
FormattedMessage(__jsx='{...messages.copy}')
Link.cmf-trash(to="#")
i.ti-trash
|
FormattedMessage(__jsx='{...messages.delete}')
.cm-icon
i(className='{data.icon}')
h2.cm-title {data.disciplineAbbreviation}
span.badge.badge-danger {data.status}
p.cm-questions {data.questionNumber}
FormattedMessage(__jsx='{...messages.questions}')
.cm-info
Link(to="#") {data.disciplineName}
Link(to="#") {data.year}
Link(to="#") {data.segment}
.cm-date
//- i.pe-7s-refresh-2
| {data.date}
And this is the file where I need to put the onClick event:
import React from 'react';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import template from './index.pug';
import '../../assets/scss/main.scss';
export default function (params = {}) {
const { messages, FormattedMessage } = params;
return (
<div>
<ul className="nav nav-tabs">
<li className="nav-item">
<a className="nav-link" href="#">
<FormattedMessage {...messages.all} />
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2018</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">2017</a>
</li>
</ul>
<div className="navigation-tabs display-none">
<a>
<i className="nt-icon ti-angle-left" />
</a>
1 de 3
<a>
<i className="nt-icon ti-angle-right" />
</a>
</div>
</div>
);
}
Thanks
You can pass any method for onClick event to any component like that:
App.js
class App extends React.Component {
handleClick = () => alert( "Clicked" );
render() {
return (
<div>
<Child click={this.handleClick}/>
</div>
)
}
}
or with a function component if you don't need lifecylce methods or "this" (here we don't need):
const App = () => {
const handleClick = () => alert( "Clicked" );
return (
<div>
<Child click={handleClick}/>
</div>
);
}
Child.js
const Child = ( props ) => (
<div>
<button onClick={props.click}>Click me!</button>
</div>
)

Pagination should not be rendered in every view in ReactJS?

I have 2 components 1)Content 2)Pagination
When I click on view stats button (see in screenshot) rendermatchinfo() method gets called and it shows details of single match and also shows pagination which should not be shown. Pagination must be shown only on home page where content component renders match details of all matches and not single match.
content.js :
import React, { Component } from 'react';
import Matchinfo from './matchinfo';
import './content.css';
class Content extends Component {
constructor(props){
super(props);
this.state = {
matches:[],
loading:true,
callmatchinfo: false,
matchid:''
};
}
componentDidMount(){
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches:res,
loading:false
});
})
}
viewstats(matchid){
this.setState({
callmatchinfo: true,
matchid: matchid
});
}
rendermatchinfo(){
return <Matchinfo matchid={this.state.matchid} />
}
renderMatches() {
return this.state.matches.slice(this.props.start, this.props.end).map(match => {
return (
<div className="col-lg-3">
<div id="content">
<p className="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div className="stats">
<button type="button" onClick= {()=>{this.viewstats(match.id)}} className="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
else if(this.state.callmatchinfo){
return <Matchinfo match_id={this.state.matchid} />
}
return (
<div>
<div className="row">
{this.renderMatches()}
</div>
<div className="row">
{this.state.callmatchinfo ? this.rendermatchinfo() : ''}
</div>
</div>
);
}
}
export default Content;
pagination.js:
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(val){
this.setState({
end:val*16,
start:end-16
});
const end = val*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
Pagination and content component are imported in layout component.
layout.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Layout extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16,
};
}
onChangePagination = (start, end) => {
this.setState({
start,
end
});
};
render() {
const {start, end} = this.state;
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Layout;
Screenshot:
Home page which shows pagination :
When I click on view stats button of any particular match it still shows pagination but it should not show it.
move Pagination to Content component
Layout.js
import React, { Component } from 'react';
import Content from './content';
class Layout extends Component {
render() {
return (
<div>
<Content />
</div>
);
}
}
export default Layout;
Content.js
import React, { Component } from 'react';
import Matchinfo from './matchinfo';
import './content.css';
import Pagination from './pagination';
class Content extends Component {
constructor(props) {
super(props);
this.state = {
matches: [],
loading: true,
callmatchinfo: false,
matchid: '',
start: 0,
end: 16,
};
}
componentDidMount() {
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches: res,
loading: false
});
})
}
onChangePagination = (start, end) => {
this.setState({
start,
end
});
};
viewstats(matchid) {
this.setState({
callmatchinfo: true,
matchid: matchid
});
}
rendermatchinfo() {
return <Matchinfo matchid={this.state.matchid} />
}
renderMatches() {
return this.state.matches.slice(this.state.start, this.state.end).map(match => {
return (
<div className="col-lg-3">
<div id="content">
<p className="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div className="stats">
<button type="button" onClick={() => { this.viewstats(match.id) }} className="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
else if (this.state.callmatchinfo) {
return <Matchinfo match_id={this.state.matchid} />
}
return (
<div>
<div className="row">
{this.renderMatches()}
{!this.state.callmatchinfo && <Pagination onChange={this.onChangePagination} />}
</div>
<div className="row">
{this.state.callmatchinfo ? this.rendermatchinfo() : ''}
</div>
</div>
);
}
}
export default Content;
You should remove Pagination component from Layout component. Content component can be renamed as Matches component. Within the Matches component, show the Pagination component.
When view stat is clicked, show the MatchInfo component and hide the Matches and Pagination component.

Pagination gets rendered for all views but it should be rendered only on home page in ReactJS?

I have created 3 components:
content
matchinfo
layout
pagination
Whenever I click on view stats button matchinfo component view is rendered which displays matchinfo of particular match. Whenever I click on view stats button (see screenshot) it also renders pagination component also which should not be rendered how can I fix this.
Component matchinfo is child component of content component.
content.js :
import React, { Component } from 'react';
import Matchinfo from './matchinfo';
import './content.css';
class Content extends Component {
constructor(props){
super(props);
this.state = {
matches:[],
loading:true,
callmatchinfo: false,
matchid:''
};
}
componentDidMount(){
fetch('api/matches')
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
matches:res,
loading:false
});
})
}
viewstats(matchid){
this.setState({
callmatchinfo: true,
matchid: matchid
});
}
rendermatchinfo(){
return <Matchinfo matchid={this.state.matchid} />
}
renderMatches() {
return this.state.matches.slice(this.props.start, this.props.end).map(match => {
return (
<div className="col-lg-3">
<div id="content">
<p className="match">MATCH {match.id}</p>
<h4>{match.team1}</h4>
<p>VS</p>
<h4>{match.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{match.winner}</h4>
</div>
<div className="stats">
<button type="button" onClick= {()=>{this.viewstats(match.id)}} className="btn btn-success">View Stats</button>
</div>
</div>
</div>
);
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
else if(this.state.callmatchinfo){
return <Matchinfo match_id={this.state.matchid} />
}
return (
<div>
<div className="row">
{this.renderMatches()}
</div>
<div className="row">
{this.state.callmatchinfo ? this.rendermatchinfo() : ''}
</div>
</div>
);
}
}
export default Content;
matchinfo.js :
import React, { Component } from 'react';
class Matchinfo extends Component {
constructor(props){
super(props);
this.state = {
info:[],
loading:true
};
}
componentWillMount(){
fetch(`api/match/${this.props.match_id}`)
.then(res => res.json())
.then(res => {
console.log(res)
this.setState({
info:res,
loading:false
})
})
}
render() {
if (this.state.loading) {
return <div>Loading...</div>
}
const {info} = this.state;
return (
<div>
<p className="match">MATCH {info.id}</p>
<h4>{info.team1}</h4>
<p>VS</p>
<h4>{info.team2}</h4>
<div className="winner">
<h3>WINNER</h3>
<h4>{info.winner}</h4>
</div>
</div>
);
}
}
export default Matchinfo;
pagination.js :
import React, { Component } from 'react';
class Pagination extends Component {
handleClick(val){
this.setState({
end:val*16,
start:end-16
});
const end = val*16;
this.props.onChange(end - 16, end);
}
render() {
return (
<div>
<div className="container">
<ul className="pagination">
<li><a href="#" onClick={this.handleClick.bind(this, 1)}>1</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 2)}>2</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 3)}>3</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 4)}>4</a></li>
<li><a href="#" onClick={this.handleClick.bind(this, 5)}>5</a></li>
</ul>
</div>
</div>
);
}
}
export default Pagination;
layout.js :
import React, { Component } from 'react';
import Pagination from './pagination';
import Content from './content';
class Layout extends Component {
constructor(props){
super(props);
this.state = {
start:0,
end:16
};
}
onChangePagination = (start, end) => {
this.setState({
start,
end
});
};
render() {
const {start, end} = this.state;
return (
<div>
<Content start={start} end={end}/>
<Pagination onChange={this.onChangePagination}/>
</div>
);
}
}
export default Layout;
Screenshots:
Onclick view stats button it still shows pagination:
You should approach toggling pagination the same way you did for showing match information. Hold a variable in this.state for your Layout component and make a method that will control that this.state variable. Pass that method down to your child component. Here is a barebones example:
class Layout extends Component {
constructor(props){
super(props);
this.state = {
showPagination: true
}
}
onChangePagination = () => {
this.setState({showPagination: !this.state.showPagination}) //toggles pagination
};
render() {
return (
<div>
{
this.state.showPagination
?
<Pagination onChangePagination={this.onChangePagination}/>
:
<button onClick={this.onChangePagination}>
show pagination
</button>
}
</div>
)
}
}
class Pagination extends Component {
handleClick() {
this.props.onChangePagination()
}
render() {
return (
<div>
<button onClick={this.handleClick}>
toggle pagination
</button>
</div>
)
}
}

Resources