How to display the name of the clicked button in React? - javascript

They are working on the game of paper, stone, scissors. I would like to display its id in Result component after pressing one of the buttons. How can I do this?
App.js
import React, { Component } from "react";
import "./App.scss";
import SubmitInfo from "./SubmitInfo";
import ResultInfo from "./ResultInfo";
class App extends Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
}
test = id => {
//return <Result id={this.props.id}></Result>
console.log("test");
};
render() {
return (
<div>
<div className="board">
<div className="title_row">
<h1 className="title">Kamień, Papier, Nożyce</h1>
</div>
</div>
<div className="board">
<div className="submit_row">
<SubmitInfo id="papier" click={this.test} />
<SubmitInfo id="kamien" click={this.test} />
<SubmitInfo id="nozyce" click={this.test} />
<ResultInfo id={this.test} />
</div>
</div>
</div>
);
}
}
export default App;
SubmitInfo.js
Transfer to id props and click event, Then I render three buttons with different icons.
import React from "react";
import styles from "./submit.scss";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import {
faHandPaper,
faHandScissors,
faHandRock
} from "#fortawesome/free-solid-svg-icons";
import ResultInfo from "./ResultInfo";
function Submit({ id, click }) {
if (id === "nozyce") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandScissors} />
</button>
);
} else if (id === "papier") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandPaper} />
</button>
);
} else if (id === "kamien") {
return (
<button className="submit" onClick={click}>
<FontAwesomeIcon className="icon" icon={faHandRock} />
</button>
);
}
}
export default SubmitInfo;
ResultInfo.js
Here I would like to display the id of the button clicked.
import React, { Component } from "react";
class ResultInfo extends Component {
render(props) {
return (
<div>
{" "}
{this.props.id}
{console.log(this.props.id)}
</div>
//<div></div>
);
}
}
export default ResultInfo;

You should use state. Initialize it in the constructor
constructor(props) {
super(props);
this.state = {
id: null,
};
}
Your test function, should set the state with the id
test = id => {
this.setState({
id,
});
};
ResultInfo component should recieve the state id as an attribute
<ResultInfo id={this.state.id} />
And, the buttons on SubmitInfo component should call the click function, with the id as a parameter
<button className='submit' onClick={() => click(id)}>Rock</button>

David's explanation is correct.
I was working on a codesandbox example on parallel, figured it'd help.
You have some additional errors, like exporting default SubmitInfo instead of Submit, and you can use conditional rendering of ResultInfo component.

Related

to display a different component with each click (using hooks)

I want to display a different component with each button click.
I'm sure the syntax is wrong, can anyone help me? The browser doesn't load
I would love an explanation of where I went wrong
One component (instead of HomePage) should display on the App component after clicking the button. Help me to understand the right method.
Thanks!
App.js
import React, {useState} from 'react';
import './App.css';
import Addroom from './components/Addroom.js'
import HomePage from './components/HomePage.js'
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button onClick={()=>{setFlage({flag:true})}}>Addroom</button>
<button onClick={()=>{setFlage({flag:false})}}>HomePage</button>
{setState({flag}) && (
<div><Addroom index={i}/></div>
)}
{!setState({flag}) && (
<div><HomePage index={i}/></div>
)}
</div>
)
}
export default App;
HomePage
import React from 'react'
export default function HomePage() {
return (
<div>
HomePage
</div>
)
}
Addroom
import React from 'react'
export default function Addroom() {
return (
<div>
Addroom
</div>
)
}
I didn't test it but as i can see it should be something like this:
<button onClick={()=>setFlage(true)}>Addroom</button>
<button onClick={()=>setFlage(false)}>HomePage</button>
{flag && (
<div><Addroom index={i}/></div>
)}
{!flag && (
<div><HomePage index={i}/></div>
)}
You need to call setFlage function with argument of Boolean saying true or false and it changes the flag variable that you want to read.
Try the following.
function App() {
const [flag, setFlage] = useState(false);
return (
<div className="App">
<h1>My Smart House</h1>
<button
onClick={() => {
setFlage(true);
}}
>
Addroom
</button>
<button
onClick={() => {
setFlage(false );
}}
>
HomePage
</button>
{flag ? <Addroom /> : <HomePage /> }
</div>
);
}
You are missing render methods and also you should use setState for reactive rendering.( when you use state variables and once value changed render method will rebuild output so this will load your conditinal component.
https://jsfiddle.net/khajaamin/f8hL3ugx/21/
--- HTML
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Home</div>;
}
}
class Contact extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div> In Contact</div>;
}
}
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: false,
};
}
handleClick() {
this.setState((state) => ({
flag: !state.flag,
}));
console.log("hi", this.state.flag);
}
getSelectedComp() {
if (this.state.flag) {
return <Home></Home>;
}
return <Contact></Contact>;
}
render() {
console.log("refreshed");
return (
<div>
<h1>
Click On button to see Home component loading and reclick to load back
Contact component
</h1
<button onClick={() => this.handleClick()}>Switch Component</button>
{this.getSelectedComp()}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));

Update state between different Components on ReactJS

I am trying to update the state from an other component to an other component.
I want on header.jsx the state total to be updated when i click on add to cart button on product.jsx
Here is my code
index.jsx
import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Header from './header';
import Footer from './footer';
import Posts from './posts';
import Post from './post';
import Products from './products';
import Product from './product';
import Page from './page';
// Load the Sass file
require('./style.scss');
const App = () => (
<div id="page-inner">
<Header />
<main id="content">
<Switch>
<Route exact path={Settings.path + 'products/:product'} component={Product} />
</Switch>
</main>
<Footer />
</div>
);
// Routes
const routes = (
<Router>
<Route path="/" component={App} />
</Router>
);
render(
(routes), document.getElementById('page')
);
header.jsx
import React from "react";
import { Link } from "react-router-dom";
class Header extends React.Component {
constructor(props) {
super(props);
this.state = { products: [], total: 0 }
var total = 0;
this.cartUpdated = this.cartUpdated.bind(this)
}
componentDidMount() {
//let cart = localStorage.getItem('total');
// this.setState({ total: 100 });
}
cartUpdated()
{
this.setState({ total: cart+100 });
}
render() {
return (
<div className="cart-icon p-3 m-auto">
Cart/ Total: <span className=""><span className="cart">€</span>{this.state.total}</span><i className="fas fa-shopping-cart" />
</div>
);
}
}
export default Header;
product.jsx
import React from "react";
import NotFound from "./not-found";
import "react-image-gallery/styles/scss/image-gallery.scss";
import ImageGallery from 'react-image-gallery';
class Product extends React.Component {
constructor(props) {
super(props);
this.state = { product: {}, total: 0
};
// ACTIONS
// addToCart
this.addToCart = this.addToCart.bind(this);
}
addToCart()
{
this.props.cartUpdated;
/*
let total = localStorage.getItem('total')
? JSON.parse(localStorage.getItem('total')) : {};
localStorage.setItem('total', 100); */
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
.......
};
renderProduct() {
if (this.state.product.images) {
const images = [];
if (this.state.product) {
this.state.product.images.map((image, i) => {
var new_image = {"original":image, "thumbnail":image} ;
images.push(new_image);
});
}
return (
<div className="col-md-12">
<div className="row">
<div className="col-md-6">
<ImageGallery items={images} showPlayButton={false} showFullscreenButton={false} thumbnailPosition="left" />
</div>
<div className="col-md-6">
<h4 className="card-title">{this.state.product.name}</h4>
<p className="card-text">
<strike>${this.state.product.regular_price}</strike>{" "}
<u>${this.state.product.sale_price}</u>
</p>
<p className="card-text">
<small className="text-muted">
{this.state.product.stock_quantity} in stock
</small>
</p>
<p
className="card-text"
dangerouslySetInnerHTML={{
__html: this.state.product.description
}}
/>
<div className="superflex add_to_cart_wrapper">
<button type="submit" name="add-to-cart" value={93} className="add_to_cart btn btn-success alt" onClick={this.props.cartUpdated}>Add to cart</button>
</div>
</div>
</div>
</div>
);
}
}
renderEmpty() {
return <NotFound />;
}
render() {
return (
<div className="container post-entry">
{this.state.product ? this.renderProduct() : this.renderEmpty()}
</div>
);
}
}
export default Product;
You can do something like this to achieve this. But the more clean solution which is suggested by the React JS is to use the React Context API.
Am sharing a link from the React JS documentation which exactly have the same scenario that you want to tackle.
https://reactjs.org/docs/context.html#updating-context-from-a-nested-component
And also since you are using the React pure component function so we can use the React hooks, you can have a look here at
https://reactjs.org/docs/hooks-reference.html#usestate
so in your code it should be like this
./Total-Context.js
export const TotalContext = React.createContext({
total: 0,
setTotal: () => {
},
});
./index.jsx
import { TotalContext } from './Total-Context';
const App = () => {
const [total, setTotal] = useState(0);
return (
<TotalContext.Provider value={{total, setTotal}}>
<div id="page-inner">
<Header currentTotal={total} />
<main id="content">
<Switch>
<Route
exact
path={`${Settings.path}products/:product`}
component={Product}
/>
</Switch>
</main>
<Footer />
</div>
</TotalContext.Provider>
);
};
and Now we can use the TotalContext consumer in the Product component and call the method to set the total method in the global context like this.
./Product.jsx
import { TotalContext } from './Total-Context';
const Product = () => (
<TotalContext.Consumer>
{({total, setTotal}) => (
<button
onClick={() => {setTotal(newTotal)}}
>
Update total
</button>
)}
</TotalContext.Consumer>
)
so after calling the click method the Header component should have the updated value of the total.
you can use redux to manage state across multiple components.
getting started with redux
To Answer this:
There is no way by the help of which you can pass state between two react components, as state is private to a component.
props can help you in this regard. Props also can't be passed from child to parent it can always be from parent to child.
There is a twist by the help of which you can achieve this, please follow the below article section: "How to pass Props from child to parent Component?" to get clear idea on this:
URL: https://www.robinwieruch.de/react-pass-props-to-component/

Moving data between react components

So I'm trying to break the component on my App.js into a smaller component, that being my Sidebar.js. I took a small section of the code and put it in its own Sidebar.js file but no matter what I've tried, I cant call my function getNotesRows() from App.js without it being unable to find it or this.states.notes being undefined.
I just want it to send the code back and forth. This is a demo app, so I know it's not the most practical.
import React, { Component } from "react";
import classNames from "classnames";
import logo from "./logo.svg";
import checkMark from "./check-mark.svg";
import "./App.css";
import Sidebar from "./components/Sidebar.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
notes: [],
currentNoteIndex: 0
};
this.markAsRead = this.markAsRead.bind(this);
this.selectNote = this.selectNote.bind(this);
console.log("Test started 2.25.19 19:23");
}
componentWillMount() {
fetch('/notes')
.then(response => response.json())
.then(
notes => {
this.setState({
notes: notes,
currentNoteIndex: 0
})
}
)
.catch(
error => {
console.log('Ooops!');
console.log(error);
}
);
}
markAsRead() {
this.setState(currentState => {
let marked = {
...currentState.notes[currentState.currentNoteIndex],
read: true
};
let notes = [...currentState.notes];
notes[currentState.currentNoteIndex] = marked;
return { ...currentState, notes };
});
}
selectNote(e) {
this.setState({ currentNoteIndex: parseInt(e.currentTarget.id, 10) });
}
getTotalUnread() {
let unreadArray = this.state.notes.filter(note => {
return note.read === false;
})
return unreadArray.length;
}
getNotesRows() {
return this.props.notes.map(note => (
<div
key={note.subject}
className={classNames("NotesSidebarItem", {
selected:
this.props.notes.indexOf(note) === this.props.currentNoteIndex
})}
onClick={this.selectNote}
id={this.props.notes.indexOf(note)}
>
<h4 className="NotesSidebarItem-title">{note.subject}</h4>
{note.read && <img alt="Check Mark" src={checkMark} />}
</div>
));
}
// TODO this component should be broken into separate components.
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Notes Viewer Test App</h1>
<div>
Unread:
<span className="App-title-unread-count">
{this.getTotalUnread()}
</span>
</div>
</header>
<div className="Container">
<Sidebar />
<section className="NoteDetails">
{this.state.notes.length > 0 && (
<h3 className="NoteDetails-title">
{this.state.notes[this.state.currentNoteIndex].subject}
</h3>
)}
{this.state.notes.length > 0 && (
<p className="NoteDetails-subject">
{this.state.notes[this.state.currentNoteIndex].body}
</p>
)}
{this.state.notes.length > 0 && (
<button onClick={this.markAsRead}>Mark as read</button>
)}
{this.state.notes.length <= 0 && (
<p>
No Notes!
</p>
)}
</section>
</div>
</div>
);
}
}
export default App;
Above is my App.js
and below is the Sidebar.js that I'm trying to create
import React, { Component } from "react";
import "../App.css";
import App from "../App.js";
class Sidebar extends React.Component{
constructor(props) {
super(props);
}
render(){
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{App.getNotesRows()}</div>
</section>
)}}
export default Sidebar;
You cannot access a method like that. You need to pass the method as a prop and use it in the child.
<Sidebar getNotesRows={this.getNotesRows} />
and in Sidebar use
<div className="NotesSidebar-list">{this.props.getNotesRows()}</div>
In your sidebar, you're trying to call getNotesRows() from App, but Sidebar doesn't need access to app (you shouldn't have to import App in Sidebar.js). Instead, you should pass the function from App to your Sidebar component, and reference it from Sidebar's props.
In App.js, you'll need to bind getNotesRows and pass it to sidebar.:
<Sidebar getNotesRows={ this.getNotesRows } />
Then in Sidebar.js, you'll need to reference getNotesRows in your render method:
render() {
const notes = this.props.getNotesRows();
return (
<section className="NotesSidebar">
<h2 className="NotesSidebar-title">Available Notes:</h2>
<div className="NotesSidebar-list">{ notes }</div>
</section>
);
}
It seems like the problem here is that you are trying to use a class function as a static property, to put it simply, you have not initialized the App class when you import it into your sidebar(?), thus no static function was found on your App class so you can call App.getNotesRows() maybe you should re-think your components and separate them in container-components using a Composition Based Programming approach instead of OO approach.

React Help Needed - Components not updating when index increases

I cannot get the component displayed to update when the index increases. I am able to console the proper component now but because the onClick is below the component that needs to update, it isn't changing. Can someone help me fix my code? i think I am close but cannot figure it out for the life of me.
This sign up page is where I would like to update the component. Essentially I want to display each component in the array once the next button is clicked. Currently the function console logs everything as I want it to, it's just a matter of getting it to appear in the
it is returning an error "cannot read property 'count' of null":
import React from 'react';
import Q1Name from './questions/Q1Name';
import Q2Birthday from './questions/Q2Birthday';
import Q3City from './questions/Q3City';
import Q4YouReady from './questions/Q4YouReady';
import Q5Setting from './questions/Q5Setting';
import Q6Length from './questions/Q6Length';
import Q7Email from './questions/Q7Email';
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
i: 0
}
}
_handleClick() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
// handleIncrement() {
// this.setState({ count: this.state.count + 1});
// }}
render() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
const componentsToRender = components.map((Component, i) => (
<Component key={i} />
));
return (
<div className = "container-fluid signup-page">
<div className = "question-box">
{componentsToRender[this.state.i]}
<button type="submit" className="btn btn-custom btn-lg" onClick={() => this._handleClick}>Next Question!</button>
</div>
</div>
);
}
}
export default SignUpPage;
There are a few component types I am bringing in, age, birthday, email, and a few button clicks, etc.
import React from 'react';
class Q1Name extends React.Component {
handleSubmit(event) {
event.preventDefault();
this.props.onNext();
}
render() {
return (
<div className="questions q1" style={this.props.style}>
<h1 id="question-h1">What is your name?</h1>
<form>
<div className="form-group">
<input type="name" className="form-control text-form custom-form" id="nameInput" aria-describedby="name" placeholder="" />
</div>
{/* <button type="submit" className="btn btn-custom btn-lg" onSubmit={this.handleSubmit}>Next Question!</button> */}
</form>
</div>
);
}
}
export default Q1Name;
Here is an example of the button option component:
import React from 'react';
class Q5Setting extends React.Component {
render() {
return (
<div className="questions">
<h1 id="question-h1">What is your ideal setting?</h1>
<button type="button" className="btn btn-custom-select btn-lg">Take me to the beach!</button>
<button type="button" className="btn btn-custom-select btn-lg">Anywhere outdoors!</button>
<button type="button" className="btn btn-custom-select btn-lg">All about the city!</button>
</div>
);
}
}
export default Q5Setting;
Any help in figuring this out would be greatly appreciated!!
In your constructor initialise state
constructor(props) {
super(props)
this.state = { i: 0 }
}
Write helper method handleClick
_handleClick() {
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
Now reference componentsToRender using i in state
`componentsToRender[this.state.i]
Don't forget to call your helper function on click.
onClick = {() => this._handleClick()}
The idea is your app will only re-render when your state object changes. Follow that rule for your components you wish to re-erender on the fry.

Clear Textfield material-ui ReactJS

I have two text fields and a button using Material-UI, what I want to achieve is to clear the contents of the text fields when I click the button but I don't know how to do it, I'm new to React-JS.
This is the code I have:
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
export default class CreateLinksave extends React.Component {
render() {
return (
<div clssName="container">
<div>
<TextField floatingLabelText="Receipt Desc" />
</div>
<div>
<TextField floatingLabelText="Triggers Required" />
</div>
<RaisedButton label="Clear" />
</div>
);
}
};
Can someone please help me on this?
the text should be handled by the state
therefore you must only edit the state of the component so that your changes are shown
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
export default class CreateLinksave extends React.Component {
constructor(props){
super(props);
// initial state
this.state = this.getDefaultState();
}
getDefaultState = () => {
return { text1: '', text2: '' };
}
clear = () => {
// return the initial state
this.setState(this.getDefaultState())
}
render() {
return (
<div className="container">
<div>
<TextField
value={this.state.text1}
onChange={(e)=>{this.setState({text1: e.target.value})}}
floatingLabelText="Receipt Desc"
/>
</div>
<div>
<TextField
onChange={(e)=>{this.setState({text2: e.target.value})}}
value={this.state.text2}
floatingLabelText="Triggers Required"
/>
</div>
// use the clear function
<RaisedButton label="Clear" onClick={this.clear}/>
</div>
);
}
}
If anyone has the same issue with the functional components in React, then you have to handle the value of the Textfield component with a state.
Doesn't matter whether you use Formik library or not.
Simple control the value property of the text field using a state variable.
import React from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
const sampleTextControl = () => {
const [value, setValue] = useState(''); //Initial value should be empty
const handleSubmit = (e)=> {
alert('The value: ' + value);
setValue(''); //To reset the textfield value
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<Textfield id="standard-basic" value={value} onChange={(e)=>setValue(e.target.value)}/>
<Button variant="contained" type="submit" value="Submit">
Submit
</Button>
</form >
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
If you don't want to manage state for every text field then you should use refs:
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
export default class CreateLinksave extends React.Component {
constructor(props) {
super(props);
this.receiptRef = React.createRef('');
this.triggersRef = React.createRef('');
}
handleClick = () => {
this.receiptRef.current.value = null;
this.triggersRef.current.value = null;
}
render() {
return (
<div clssName="container">
<div>
<TextField floatingLabelText="Receipt Desc" />
</div>
<div>
<TextField floatingLabelText="Triggers Required" />
</div>
<RaisedButton label="Clear" onClick={this.handleClick}/>
</div>
);
}
};

Categories