Why does not my onClick event work? - javascript

I used this.handleClick in onClick but it alerts a mistake.(this is not difined)
var Buttons = React.createClass({
getInitialState() {
return {
field: ':P '
}
},
handleClick(field) {
return this.setState = {
field
}
},
render() {
let count = 1;
return ( < div >
< h1 > {
this.state.field
} < /h1> {
buttonValues.map(function(val) {
return <button key = {
count++
}
onClick = {
Buttons.handleClick
} > {
val
} < /button>
})
} < /div>
)
}
})
ReactDOM.render( < Buttons / > , document.getElementById('app'));

You have to replace
this.setState = {field}
by
this.setState({field: field})

You cannot use Buttons.handleClick() directly because Buttons is a class. You need to instantiate the class before accessing it's method. And This won't work with React.
So, the best way is to use this to access the local methods within the class. And this.setState() is a function, to which you need to pass the new state object. Hope it helps!
<script src="https://unpkg.com/babel-core#5.8.38/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
<script type="text/babel">
var buttonValues = ['hello','there']
var Buttons = React.createClass({
getInitialState() {
return {
field: ':P '
}
},
handleClick(field) {
this.setState({
field
})
},
render() {
var _this = this
return ( < div >
< h1 > {
this.state.field
} < /h1> {
buttonValues.map(function(val, i) {
return <button key = {
i
}
onClick = {() =>
_this.handleClick(val)
} > {
val
} < /button >
})
} < /div>
)
}
})
ReactDOM.render( < Buttons / > , document.getElementById('app'));
</script>

you are not passing any arguments to your function, also use this.handleClick instead of calling the class.
onClick = {this.handleClick.bind(null, val)}
also setState is a function
this.setState({field: field})

Related

Make the loop run after "return" statement. [React JS]

Aim: I want to render 64 <Square /> elements with <br/> after each 8 elements
Problem: The script outputs just one block and then stops and nothing more happens.
Source Code:
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
class Square extends React.Component {
render() {
return <div id="block"></div>;
}
}
class Board extends React.Component {
renderSquare() {
for (let i = 1; i <= 64; i++) {
if (i % 8 == 0) {
return <br />;
} else {
return <Square />;
}
}
}
render() {
return <div>{this.renderSquare()}</div>;
}
}
function Game() {
return (
<div id="board">
<Board />
</div>
);
}
ReactDOM.render(<Game />, document.getElementById('root'));
Using return statement will exit the function after first match, hence in first loop it exits and only displays 1st element, you need to use an array, store the elements in it, and then return the array.
Change your renderSquare method to following
renderSquare() {
const items = [];
for (let i = 1; i <= 64; i++) {
if (i % 8 === 0) {
items.push(<br />);
} else {
items.push(<Square />);
}
}
return items;
}
Update
if you want to print 64 times then use the following
renderSquare() {
const items = [];
for (let i = 1; i <= 64; i++) {
items.push(<Square />);
if (i % 8 === 0) {
items.push(<br />);
}
}
return items;
}
Check this https://codesandbox.io/s/peaceful-brattain-pj32le?file=/src/App.js

Count using useState doesnt update state until question 6 even when answered correctly react

im trying to create a quiz app in react using hooks. i want the answers to be clickable and once clicked the user will then move onto the next question. My problem is that, the score state isnt updating until question 6! i am making an API call to get the questions and answers using useEffect. i know that useState is asynchronous and thats why state doesnt update straight away, its just even if i answer the first 6 questions correctly, after question 6 the score is still showing 1. Does anyone have a way around this?
My API call using useEffect:
useEffect(() => {
axios.get("https://my-quiz-server.herokuapp.com/api/newq").then((res) => {
const allQuestions = res.data;
setResult([allQuestions]);
})
.catch((error) => {
console.log(error);
});
}, [setResult]);
My Quiz component:
import React, {
useState,
useContext,
useEffect
} from "react";
import {
QuizContext
} from "../Helpers/context";
const MainQuiz = () => {
const {
score,
setScore,
result
} = useContext(QuizContext);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [optionChosen, setOptionChosen] = useState("");
console.log(optionChosen);
console.log(result);
const nextQuestion = (correctAnswer) => {
if (optionChosen === correctAnswer) {
setScore((score) => score + 1);
}
setCurrentQuestion((currentQuestion) => currentQuestion + 1);
};
useEffect(() => {
console.log("score updated", score);
}, [score]);
return ( <
div className = "quiz" > {
result.map((question, index) => {
if (currentQuestion < question.allQuiz.length) {
return ( <
h3 key = {
index
} > {
question.allQuiz[currentQuestion].q_prompt
} < /h3>
);
} else {
return null;
}
})
} <
div className = "answer__container" > {
result.map((answers, index) => {
if (currentQuestion < answers.allQuiz.length) {
return ( <
div className = "answer__options" > {
" "
} <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("a");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].a
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("b");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].b
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("c");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].c
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("d");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].d
} <
/button> <
/div>
);
} else {
return null;
}
})
} <
/div> <
span > {
score
} < /span> <
/div>
);
};
export default MainQuiz;

need to find a code to call two functions when div is clicked in react

I basically need to create an array or make a list of items that have been clicked in this div imgCard. its for a game where i have to update score when a button is clicked. If its clicked more than once the game ends. My only guess is that I should pass two functions on onClick and create an array of items already clicked.
import React, { Component, Fragment } from 'react'
import Header from './components/header'
import characters from "./characters.json"
import ImageCard from "./components/ImageCard"
import Wrapper from './components/wrapper'
class Game extends Component {
state = {
characters,
Header,
}
shuffle = () => {
var array = this.state.characters;
var ctr = array.length;
var temp;
var index;
var isClicked = []
while (ctr > 0) {
index = Math.floor(Math.random() * ctr);
ctr--;
temp = array[ctr];
array[ctr] = array[index];
array[index] = temp;
}
this.setState({
characters: array
});
};
render() {
return (
<Fragment>
<Header />
<Wrapper>
<div className="imgContainer" >
{this.state.characters.map(character => (
<div className="imgCard" onClick={this.onClick} showAlert={this.id}>
<ImageCard
key={character.id}
image={character.image}
width={'120px'}
>
</ImageCard >
</div>
))}
</div>
</Wrapper>
</Fragment>
);
}
}
export default Game;
<div className="imgCard"
onClick={()=>{
this.firstFunction();
this.secondFunction(); //two functions are called in onClick callback
}}
showAlert={this.id}>
<ImageCard
key={character.id}
image={character.image}
width={'120px'}
>
</ImageCard >
</div>
You can just call a function which contains the two function you want to run. With your code it would look something like below:
class Game extends Component {
state = {
characters,
Header,
}
shuffle = () => {
var array = this.state.characters;
var ctr = array.length;
var temp;
var index;
var isClicked = []
while (ctr > 0) {
index = Math.floor(Math.random() * ctr);
ctr--;
temp = array[ctr];
array[ctr] = array[index];
array[index] = temp;
}
this.setState({
characters: array
});
};
buttonOnClickHandler = () => {
// Destructure state or props here.
//Performe anything else
//run the functions
this.firstFunctionYouWantToRun();
this.secondFunctionYouWantToRun();
};
render() {
return (
<Fragment>
<Header />
<Wrapper>
<div className="imgContainer" >
{this.state.characters.map(character => (
<div className="imgCard" onClick={this.buttonOnClickHandler} showAlert={this.id}>
<ImageCard
key={character.id}
image={character.image}
width={'120px'}
>
</ImageCard >
</div>
))}
</div>
</Wrapper>
</Fragment>
);
}
}
export default Game;
Like this, you can call
onClick={() => {this.onClick() ; this.somefucntionname()}}

How to access component methods in React?

I am working on React.
I have a method called on_render_cell, where I have created a div and I am inserting image in that div based on some conditions.
This is the approach I am following to insert image in the div.I get the desired images. Now my goal is:
To open a modal when the image is clicked. When I try to assign onClick={this.openModal()}, the modal doesn't pops up. I noticed that this is because this points to the <img>. Can anyone help me how can I call openModal() on click handler of image tag written in result variable of on_render_cell()?
export class ScheduleManager extends SampleBase {
constructor() {
super(...arguments);
this.state = {
visible : false
}
}
openModal() {
this.setState({
visible : true
});
}
closeModal() {
this.setState({
visible : false
});
}
on_render_cell(args) {
let ele = document.createElement('div');
var sum=0;
var cursor=Event.find({Teacher:teacherName});
cursor.forEach(function(event){
if(convert(event.StartTime)===convert(args.date))
sum = sum + parseFloat(event.Duration);
});
let hoursPerDay = sum/60;
let percentHours=(hoursPerDay/maxHoursPerDay)*100;
let result ='';
if(percentHours===0)
{
result='<img id="aaa" ref="abc" className="weather-image" height="25px" width="25px" src="http://www.clker.com/cliparts/h/e/s/t/j/U/grey-circle.svg.thumb.png" onClick={console.log(this)} />';
}
else if(percentHours<=25)
{
result = '<img className="weather-image" src= "http://www.clker.com/cliparts/0/w/P/n/G/3/blue-circle-th.png" />';
}
else if(percentHours>25 && percentHours<=50)
{
result='<img className="weather-image" src= "http://www.clker.com/cliparts/o/b/y/x/Z/c/yellow-dot-th.png" />';
}
else if(percentHours>50 && percentHours<=75)
{
result = '<img className="weather-image" src= "http://www.clker.com/cliparts/1/A/W/q/h/h/orange-circle-th.png" />';
}
else
{
result='<img className="weather-image" src= "http://www.clker.com/cliparts/3/m/Q/u/P/J/red-circle-small-new.svg.thumb.png" />';
}
ele.innerHTML = result;
(args.element).appendChild(ele.firstChild);
}
render(){
return (
<div>
<input type="button" value="Open" onClick={() => this.openModal()} />
<Modal
visible={this.state.visible}
width="400"
height="300"
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div>
<h1>Title</h1>
<p>Some Contents</p>
<a href="javascript:void(0);" onClick={() => this.closeModal()}>Close</a>
</div>
</Modal>
<p>Grey circle denotes no classes for the day</p>
<p>Blue circle denotes less than 25% occupancy for the day</p>
)
}
}
Goal : Open Modal on image click. i.e, access openModal() of component
You can give some var like myRef to your componen as a prop, then in onComponentDidMount of your component assign to prop myRef current instance (this). this.props.myRef = this, then in your parent component you can use methods of myRef. For more you can check this post React.js - access to component methods
sorry but you are not doing it right. when you write:
onClick={() => this.openModal()}
you are creating a new function each time the element renders. instead, I suggest you to bind the method inside the constructor:
this.openModal = this.openModal.bind(this);
and inside the input write:
onClick={this.openModal}
this way you won't create a new function each time the elements renders, and the openModal this will point to the element itself.
If I understand correctly the problem is in this line:
result='<img id="aaa"... onClick={console.log(this)} />';
have you tried pass JSX instead of a string? bind the function openModal in the constructor, and pass to the img the function this.openModal. I think that if you will pass JSX instead of a string it will work
UPDATE
ok so I took the code from Here and edited it a bit:
export class CellTemplate extends SampleBase {
getCellContent(date) {
if (date.getMonth() === 10 && date.getDate() === 23) {
return ( < div > < img src = "src/schedule/images/thanksgiving-day.svg" / > < div className = "caption" > Thanksgiving day < /div></div > );
}
cellTemplate(props) {
if (props.type === "monthCells") {
return ( < div className = "templatewrap" > {
this.getCellContent(props.date)
} > < /div>);
}
return;
}
render() {
return ( < div className = 'schedule-control-section' >
<
div className = 'col-lg-12 control-section' >
<
div className = 'control-wrapper' >
<
ScheduleComponent cssClass = 'cell-template'
width = '100%'
height = '650px'
selectedDate = {
new Date(2017, 11, 15)
}
cellTemplate = {
this.cellTemplate.bind(this)
} >
<
ViewsDirective >
<
ViewDirective option = 'Month' / >
<
/ViewsDirective> <
Inject services = {
[Month, Resize, DragAndDrop]
}
/> <
/ScheduleComponent> <
/div> <
/div> <
/div>);
}
}
in cellTemplate you can see how to call getCellContent correct, and in getCellContent you can see how I would have return JSX.

reactjs setState before render on update

I have a react component and the requirement is to setState before render on update (url hash change). Below is code snippet:
componentConfig: function() {
.....
this.setState({rows: rows});
this.setState({loadMoreBtn: loadMoreIsVisible})
},
I was it working before, I was calling this method from getInitialState and it was working fine. getInitialState fires only once so I was not able to update it on url change. I tried various other inbuilt update methods such as componentWillReceiveProps but they are one step behind. Seems like render happens before this method gets called. I also tried to call it from render but obviously states get confused and it breaks.
As this image demonstrates, componentRillReceiveProps always behind render. I need something that fires before render each time on url update. Hope it makes sense.
or in other words, I would like to fire getInitialState on hash change.
var React = require('react'),
projectsData = require('./../projects'),
ProjectsRow = require('./projects_row'),
itemsInRow = 3,
noOfDefaultRows = 2,
projects = [],
currentProjects = [],
currentPageRows,
currentParamKey;
var Projects = React.createClass({
componentWillMount: function() {
this.componentConfig(this.props);
},
componentWillReceiveProps: function(nextProps){
this.componentConfig(nextProps);
},
componentConfig: function(props) {
var rows = [],
currentParamKey = 'projects',
loadMoreIsVisible = true,
i;
if(props.params.key) {
currentParamKey = props.params.key;
}
projects = projectsData.getByKey(currentParamKey);
projectsData.currentState[currentParamKey] = noOfDefaultRows;
currentProjects = projects.slice(); //Create a copy or array
noOfDefaultRows = projectsData.currentState[currentParamKey] || noOfDefaultRows;
for (i = 0; i < noOfDefaultRows; i++) {
if(currentProjects.length) {
rows.push(currentProjects.splice(0, itemsInRow));
}
}
currentProjects.length ? loadMoreIsVisible = true : loadMoreIsVisible = false;
this.setState({rows: rows});
this.setState({loadMoreBtn: loadMoreIsVisible})
console.log('Finished executing componentConfig and currentParamKey = ' ,currentParamKey);
},
loadMoreProjects: function(e) {
e.preventDefault();
var addRow = this.state.rows;
if(currentProjects.length) {
currentPageRows++;
addRow.push(currentProjects.splice(0, itemsInRow));
this.setState({rows: addRow});
}
if(!currentProjects.length) {
this.setState({loadMoreBtn: false})
}
},
render: function() {
console.log('Now in render and currentParamKey = ' ,currentParamKey);
var projectUrl;
//currentParamKey = this.props.params.key;
if(currentParamKey === 'projects') {
projectUrl = '#/project';
} else {
projectUrl = '#/project/' + currentParamKey
}
return (
< div className="projects">
< div className = "jumbotron" >
< div className = "container" >
< h1 > Projects < /h1>
< /div>
< /div>
< div className = "container" >
{this.state.rows.map(function(row, i) {
return <ProjectsRow url={projectUrl} row={row} key={i} />
}.bind(this))}
< /div>
< div className = "container text-center" >
<a id="loadMore" className= {this.state.loadMoreBtn ? 'linkStyle1' : 'hide'}
onClick = {this.loadMoreProjects}
role="button" > <i className="fa fa-angle-down"></i><span>Load More Projects</span>
</a>
<br />
<br />
<div className="contact-me-link">
<a className="linkStyle1" href="#/contact">
<i className="fa fa-angle-right"></i><span>Contact</span>
</a>
</div>
</div>
< /div>
);
}
});
module.exports = Projects;
The componentWillReceiveProps get new props as a parameter. The old props remain unchanged on this method execution, so you need to initialize your state based on this parameter. Try passing the props as a parameter to your componentConfig method:
componentWillMount: function() {
this.componentConfig(this.props);
},
componentWillReceiveProps: function(nextProps){
this.componentConfig(nextProps);
},
componentConfig: function(data) {
...
}

Categories