Function is not being called as it should in React onClick() - javascript

Basically, i created a button to call a function when the button is clicked, it's working, the console.log() shows the message "Working", but the script inside it, it's not being shown on the browser. I have no idea what's wrong, could someone help me? I'm very new in Software Engineer and started learning React a few days ago, so i dont know too much about it.
This is the entire code:
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
questions: Questions
}
}
Game() {
const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
console.log('Working');
return (
<div>
<GameParagraph value={item.ques} />
{
items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}
/>
</div>
</div>
))
}
</div>
)
}
render() {
return (
<StartGame
onClick={() => this.Game()}
/>
);
}
}
And that's the button to call the Game():
class StartGame extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Start</button>
);
}
}

You can try like this:
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
questions: Questions,
hasGameStarted: false
}
}
Game() {
const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
console.log('Working');
return (
<div>
<GameParagraph value={item.ques} />
{
items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}
/>
</div>
</div>
))
}
</div>
)
}
startGameClicked() {
this.setState({hasGameStarted: true});
}
render() {
return (
<StartGame
onClick={this.startGameClicked}
/>
{this.state.hasGameStarted ? this.Game() : null}
);
}
Make sure you are binding the onClick event in StartGame component properly.

You need to refactor your code. Returning JSX from an event handler (which is what your function Game is) won't cause your component to render.
Instead, the "React" way to handle this is to provide some state variable that can be updated by the super class to cause a render. i.e.
class QuizGame extends React.Component {
constructor() {
super();
this.state = {
started: false,
questions: Questions
}
}
startGame() { // JavaScript functions should be camelCase
this.setState({started: true});
}
render() {
if (this.state.started) {
return const { questions } = this.state;
const item = questions[Math.floor(Math.random() * questions.length)];
const items = [item.a, item.b, item.c, item.d];
return (
<div>
<GameParagraph value={item.ques} />
{items.map(quest => (
<div key={quest}>
<div>
<GameButton
value={quest}
onClick={() => {
if(item.ans === quest) {
return console.log('Working');
} else {
return console.log('not working')
}
}}/>
</div>
</div>
))}
</div>);
} else {
return <StartGame onClick={() => this.startGame()} />;
}
}
}

Related

I want to create some program with react js, about adding strings in window, but I am having an error

I am creating a program with React Js, where I need to write something in prompts and it needs to appear in window. Now I have created the function of adding prompts and pasting it in window, but it's giving an error.
please help me if you can : )
export default class Clock extends React.Component {
state = {items: ['Hakob', 'Arman']};
Add(){
const newitems = this.state.items.concat([prompt('a')])
this.setState({items:newitems})
}
render(){
return <div>
<Clock2/>
</div>
}
}
class Clock2 extends React.Component {
render(){
return(
<>
<button onClick={this.Add}>click</button>
<div> {this.state.items.map((e, i) => {
return <div key = {e + i}> {e} </div>
} )} </div>
</>
)
}
}
you have not defined any state in class clock2 so, the line # 798 giving you an error for cannot read property of items as it is not defined in class
class Clock2 extends React.Components {
state = {
items : //
}
}
and the second error is you are trying to return in the return function that is not correct if you want to map items you have to define map function in render
{
const items = this.state.items.map((e,i ) => {
//
}
return (
<items/>
)
So let's write your code here.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return <div><Clock2/><div>
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
You made a mistake, state are internals to component, as well as method.
This should work
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (
<>
<button onClick={this.Add}>Click</button>
<div>{ this.state.items.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}
Or you can pass values from top component to his child.
export default class Clock extends React.Component {
state = { items: ['Hakob', 'Aram']};
Add() {
const newItems = this.state.items.concat([prompt('a')])
this.setState({items:newItems})
}
render() {
return (<div><Clock2 add={this.Add} values={this.state.items}/><div>)
}
}
class Clock2 extends React.Component {
render() {
return (
<>
<button onClick={this.prop.add}>Click</button>
<div>{ this.props.values.map( (e,i) => {
return <div key={ e + i}>{e}</div>
})}
</div>
</>
)
}
}

Updating parent state from child components not working in reactjs

I was going through react official documentation when I struck upon an example which updates the parent component through child component callbacks. I was able to understand how the flow works. However, when I tried to optimize the code further it failed to update the component via callbacks.
The Original Code:
https://codepen.io/gaearon/pen/QKzAgB?editors=0010
My code change:
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
this.button = <MyButton message="Login" onClick={this.handleLoginClick} />;
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
if (isLoggedIn) {
this.button = <MyButton message="Logout" onClick={this.handleLogoutClick} />;
} else {
this.button = <MyButton message="Login" onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{this.button}
</div>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.message=props.message;
this.click=props.onClick;
}
render() {
return (
<button onClick={this.click}>
{this.message}
</button>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Ok the main problem here is that you are trying to assign to many things to "this".
React does not track changes and re-renders when component's method or properties changes.
try to avoid this pattern and use state and props directly.
Only changes to state or props will cause a component to re-render.
In you situation you can look at this code:
class LoginControl extends React.Component {
state = {isLoggedIn : false}
handleLoginClick = () => {
this.setState({isLoggedIn: true});
}
handleLogoutClick = () => {
this.setState({isLoggedIn: false});
}
button = () => {
const message = this.state.isLoggedIn ? "Logout" : "Login";
const onClick = this.state.isLoggedIn ? this.handleLogoutClick : this.handleLoginClick;
return <MyButton message={message} onClick={onClick} />
}
render() {
return (
<div>
<Greeting isLoggedIn={this.state.isLoggedIn} />
{this.button()}
</div>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.message=props.message;
this.click=props.onClick;
}
render() {
return (
<button onClick={this.props.onClick}>
{this.props.message}
</button>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);

Show and Hide specific component in React from a loop

I have a button for each div. And when I press on it, it has to show the div with the same key, and hide the others.
What is the best way to do it ? This is my code
class Main extends Component {
constructor(props) {
super(props);
this.state = {
messages: [
{ message: "message1", key: "1" },
{ message: "message2", key: "2" }
]
};
}
handleClick(message) {
//something to show the specific component and hide the others
}
render() {
let messageNodes = this.state.messages.map(message => {
return (
<Button key={message.key} onClick={e => this.handleClick(message)}>
{message.message}
</Button>
)
});
let messageNodes2 = this.state.messages.map(message => {
return <div key={message.key}>
<p>{message.message}</p>
</div>
});
return <div>
<div>{messageNodes}</div>
<div>{messageNodes2}</div>
</div>
}
}
import React from "react";
import { render } from "react-dom";
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
messages: [
{ message: "message1", id: "1" },
{ message: "message2", id: "2" }
],
openedMessage: false
};
}
handleClick(id) {
const currentmessage = this.state.messages.filter(item => item.id === id);
this.setState({ openedMessage: currentmessage });
}
render() {
let messageNodes = this.state.messages.map(message => {
return (
<button key={message.id} onClick={e => this.handleClick(message.id)}>
{message.message}
</button>
);
});
let messageNodes2 = this.state.messages.map(message => {
return (
<div key={message.key}>
<p>{message.message}</p>
</div>
);
});
const { openedMessage } = this.state;
console.log(openedMessage);
return (
<div>
{openedMessage ? (
<div>
{openedMessage.map(item => (
<div>
{" "}
{item.id} {item.message}{" "}
</div>
))}
</div>
) : (
<div> Not Opened</div>
)}
{!openedMessage && messageNodes}
</div>
);
}
}
render(<Main />, document.getElementById("root"));
The main concept here is this following line of code.
handleClick(id) {
const currentmessage = this.state.messages.filter(item => item.id === id);
this.setState({ openedMessage: currentmessage });
}`
When we map our messageNodes we pass down the messages id. When a message is clicked the id of that message is passed to the handleClick and we filter all the messages that do not contain the id of the clicked message. Then if there is an openedMessage in state we render the message, but at the same time we stop rendering the message nodes, with this logic {!openedMessage && messageNodes}
Something like this. You should keep in state only message key of visible component and in render method you should render only visible component based on the key preserved in state. Since you have array of message objects in state, use it to render only button that matches the key.
class Main extends Component {
constructor(props) {
super(props);
this.state = {
//My array messages: [],
visibleComponentKey: '',
showAll: true
};
handleClick(message) {
//something to show the specific component and hide the others
// preserve in state visible component
this.setState({visibleComponentKey : message.key, showAll: false});
};
render() {
const {visibleComponentKey, showAll} = this.state;
return (
<div>
{!! visibleComponentKey && ! showAll &&
this.state.messages.filter(message => {
return message.key == visibleComponentKey ? <Button onClick={e => this.handleClick(message)}>{message.message}</Button>
) : <div /> })
}
{ !! showAll &&
this.state.messages.map(message => <Button key={message.key} onClick={e => this.handleClick(message)}>{message.message}</Button>)
}
</div>
);
}
}
I haven't tried it but it gives you a basic idea.
I cannot reply to #Omar directly but let me tell you, this is the best code explanation for what i was looking for! Thank you!
Also, to close, I added a handleClose function that set the state back to false. Worked like a charm!
onCloseItem =(event) => {
event.preventDefault();
this.setState({
openedItem: false
});
}

React calling methods in different conditions

I'm beginner on react and i've written the code below:
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
save() {
this.props.onChange(this.refs.newVal.value, this.props.id);
this.setState({editing: !this.state.editing});
// console.log('save is over');
}
renderForm() {
return (
<div className="note">
<textarea ref="newVal"></textarea>
<button onClick={this.save}>SAVE</button>
</div>
);
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>EDIT</button>
<button onClick={this.remove}>X</button>
</span>
</div>
);
}
render() {
console.log(this.state.editing);
return (this.state.editing) ? this.renderForm()
: this.renderDisplay()
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
In render(), onClick event has a function, that is, if used in this way: {this.add} the following error is created:
Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, ...})
Why? while in the eachNote() method this command is used:
onChange={this.update}
And there was no error.
Someone can tell me the reason? thanks.
The problem is that in the add function you are taking an argument text and setting it in the state so when you call onClick={() => this.add()}, you are not passing any argument to add function and hence in its definition text is undefned and hence state note is set as undefined.
However if you directly call it like onClick={this.add} , the add function receives the event object as a parameter and hence it sets state note to be an event object which you are using to render
onClick={this.add} will pass the click event to this.add.
So what you need to do is either:
onClick={e => this.add('some text')} or similar.
If you want to onClick={this.add} you have to ensure that your add method is: add(event) { ... } instead.
The <Note /> component does not contain a render() method to return anything. Add a render() method and return something.
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
render() {
return (
<div>Render something</div>
)
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>

React remove element from onclick

When a user deletes an item from their cart, I have the item displayed with a button to add it back to the cart. This works. Once the user adds the item back to their cart, I want the item in the display component to be removed. Here is my code for reference.
CART:
class Cart extends Component {
constructor(props) {
super(props);
this.state = {differences: [],};
}
componentWillReceiveProps(nextProps){
let thisProps = this.props.cart.items;
let theNextProps = nextProps.cart.items;
if (thisProps.map(i => i.sku).some(item => !theNextProps.map(i => i.sku).includes(item))) {
let diff = [thisProps.filter(item => !theNextProps.includes(item))];
this.setState({differences: this.state.differences.concat(diff)});
}
}
...
render = () => {
<CartAddBack data={this.state.differences} onAddToCart={this.props.addToCart} />
<CheckoutSection className='Checkout-cart-items' titleKey='checkout.items.title'>
{this.props.cart.items.map((item) => {
return (
<CheckoutItem item={item} key={item.sku} onRemoveProduct={this.props.removeFromCart} onUpdateQuantity={this.props.updateCartItem}/>
);
})}
</CheckoutSection>
}
}
CartAddBack:
class CartAddBack extends Component {
constructor() {
super();
this.state = {deleted: null};
this.onDelete = this.onDelete.bind(this);
}
onDelete(id){
console.log("THE SKU SHOULD BE HERE", id);
this.setState(id);
}
render() {
let {data} = this.props;
let theData = data.map(i => parseInt(i[0].sku));
let theStated = this.state.deleted;
return (
<div>
{data &&
<div className="CartAddBack">
<div className="CartAddBack-Wrapper">
<ul className="CartAddBack-Item-ul">
{theStated != null
? theData.filter(i => !theStated.includes(i)) &&
<CartAddBackItem data={item[0]} onAddToCart={this.props.onAddToCart} onDelete={this.onDelete}/>
: data.map((item) => {
return <CartAddBackItem data={item[0]} onAddToCart={this.props.onAddToCart} onDelete={this.onDelete}/>
})
}
</ul>
</div>
</div>
}
</div>
)
}
}
CartAddBackItem:
class CartAddBackItem extends Component {
constructor() {
super();
this.onClick = this.onClick.bind(this);
}
onDelete(){
this.props.onDelete({deleted: this.props.data.sku})
}
allowSubmit() {
this.setState({
allowSubmit: true,
});
}
onClick() {
if (this.props.data) {
if (this.props.data.quantity <= this.props.data.inventory_quantity) {
const success = () => {
this.allowSubmit();
},
failure = (err) => {...};
this.props.onAddToCart({
...{sku: this.props.data.sku, quantity: this.props.data.quantity}, quantity: this.props.data.quantity}).then(success, failure);
}
else {
this.setState=({display: false});
const success = () => {
this.allowSubmit();
},
failure = (err) => {...};
this.props.onAddToCart({
...{sku: this.props.data.sku, quantity: this.props.data.quantity}, quantity: 1}).then(success, failure);
}
}
}
render() {
let {data} = this.props;
return (
<li className="CartAddBackItem">
{data &&
<div className="CartAddBackItem-Wrapper">
<Button className="CartAddBackItem-button" onClick={this.onClick}><FormattedMessage id="cart.cartAddBack"/></Button>
<Link to={`product/${data.sku}`} className="CartAddBackItem-Link">
<p className="CartAddBackItem-title">{data.title}</p>
</Link>
</div>
}
</li>
)
}
}
I want CartAddBack to remove CartAddBackItem if the item was clicked in CartAddBackItem. Only thing I havent tried that I just thought about was to make a componentWillReceiveProps inside CartAddBack. But there has to be a better way. Issue I'm running into is my mapping items into CartAddBackItem. The gross looking {theStated != Null ? theData.filter(i =>... allows me to add items to the cart. It works if it was only data.map((item)=>... but I want to show my thinking. Any advice?

Categories

Resources