This is not firing on the component but when I attach my event handler to a div it works. Do I need to pass a prop types function in my child component?
const buttonStyle = {
color: 'red'
};
class Button extends React.Component {
render() {
return (
<a className="social-button twitter">
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
class PanelButtons extends React.Component {
constructor(props){
super(props);
}
handleClick() {
console.log('this is:');
}
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons">
<Button onClick={(e) => this.handleClick(e)} />{/*does now work attaching it to a component*/}
</div>
</div>
)
}
}
ReactDOM.render(<PanelButtons />, document.querySelector('body'));
What you did basically, is passing a callback called onClick to the Button component. It will be accessible to you through the component's props.
class Button extends React.Component {
render() {
return (
<a className="social-button twitter" onClick={this.props.onClick}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
once the Button component's a element is clicked, the callback that you passed will be triggered (and handleClick will be called).
You should pass down the props into <Button /> component
class Button extends React.Component {
render() {
return (
<a className="social-button twitter" {...this.props}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
More reading: JSX spread attributes
An onClick on a <button /> would have worked as you expected.
But this is <Button /> a component which you created, the onClick will be sent as props which you can invoke through an onClick on the Button component's a tag like below, whose handleClick will callback the actual onClick on your PanelButtons component.
const buttonStyle = {
color: 'red'
};
class Button extends React.Component {
handleClick = (e) => {
this.props.onClick(e)
}
render() {
return (
<a className="social-button twitter" onClick={this.handleClick}>
<i href="#" className="fa fa-twitter"></i>
</a>
)};
}
class PanelButtons extends React.Component {
constructor(props){
super(props);
}
handleClick() {
console.log('this is:');
}
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons">
<Button onClick={(e) => this.handleClick(e)} />{/*does now work attaching it to a component*/}
</div>
</div>
)
}
}
ReactDOM.render(<PanelButtons />, document.querySelector('body'));
If you just want to add an onClick in the PanelButtons for each button, just modify your render a little bit like this by adding event listener on the div tag.
render() {
return (
<div>
<div onClick={(e) => this.handleClick(e)}> {/*this works attaching it to a div*/}
CLick me
</div>
<div className="social-buttons" onClick={(e) => this.handleClick(e)}>
<Button />{/*does now work attaching it to a component*/}
</div>
</div>
)
}
Related
How to return my div and my variable view to another component? I can't do 2 returns?
class Toolbar extends Component {
constructor(){
super();
this.state = {
view:''
}
}
render(){
return(
<div className="button-toolbar">
<button className="button" onClick={() => this.setState({view: 1})}>1</button>
<button className="button" onClick={() => this.setState({view: 2})}>2</button>
<button className="button" onClick={() => this.setState({view: 3})}>3</button>
<button className="button">Wallet</button>
</div>
)
return {this.state.view}
I want to return the value of my button to send it into another component
App.js :
render() {
return (
<div className="App">
<Toolbar></Toolbar>
<View view=/*Return toolbar value*/></View>
</div>
);
}
If you want the this.state.view to show alongside the buttons, you can simply include it in the same render return:
class Toolbar extends Component {
constructor(){
super();
this.state = {
view:''
}
}
render(){
return(
<div className="button-toolbar">
<button className="button" onClick={() => this.setState({view: 1})}>1</button>
<button className="button" onClick={() => this.setState({view: 2})}>2</button>
<button className="button" onClick={() => this.setState({view: 3})}>3</button>
<button className="button">Wallet</button>
{this.state.view}
</div>
)
}
}
If you want to not show the buttons once the view value is set, you can just return early if a condition is met.
class Toolbar extends Component {
constructor(){
super();
this.state = {
view:''
}
}
render(){
if (this.state.view) {
return this.state.view;
}
return (
<div className="button-toolbar">
<button className="button" onClick={() => this.setState({view: 1})}>1</button>
<button className="button" onClick={() => this.setState({view: 2})}>2</button>
<button className="button" onClick={() => this.setState({view: 3})}>3</button>
<button className="button">Wallet</button>
</div>
)
}
}
Edit: Sharing state between components
If you need to share this state between a sibling component, you should generally raise the state up to the nearest common parent. In this case, that's App.js. It might look like this:
App.js
class App extends Component {
constructor() {
this.state = { view: "" };
this.setView = this.setView.bind(this);
}
setView(view) {
this.setState({ view });
}
render() {
return (
<div className="App">
<Toolbar setView={this.setView}></Toolbar>
<View view={view}></View>
</div>
);
}
}
And then your Toolbar component wouldn't need state anymore, but would use this.props.setView to set the view on the parent component.
Toolbar.js
class Toolbar extends Component {
render(){
return(
<div className="button-toolbar">
<button className="button" onClick={() => this.props.setView(1)}>1</button>
<button className="button" onClick={() => this.props.setView(2)}>2</button>
<button className="button" onClick={() => this.props.setView(3)}>3</button>
<button className="button">Wallet</button>
</div>
)
}
}
I reuse the Chat component twice into another component. It display when you click Chat button but it overlaps each other.
class Chat extends React.Component {
constructor() {
super();
this.state = {
show: false,
};
}
reset = () => {
this.setState(false);
}
open = () => {
this.setState({ show: true });
}
close = () => this.setState({ show: false });
render() {
return (<div className="chat">
<button className="btn-yes round" onClick={this.open}>{this.props.title}</button>
{this.state.show &&
<div className="show-chat">
<div className="chat-head">Now Chatting <i className="fas fa-angle-down" onClick={this.close}></i></div>
<div className="chat-body">
<div className="blue">Teresa wants to chat about her healthcare finances</div>
<ul>
<li><img src={agentPhoto} alt="chat agent avatar" /></li>
<li>
<h6>John Newman</h6>
<div className="gray">Hi Teresa!</div>
<div className="gray">Here is the link to the finance tool we discussed.</div>
<div className="gray">If you have any questions, let me know!</div>
</li>
</ul>
</div>
<input placeholder="Type here and hit enter to chat"></input>
</div>}
</div>);
}
}
I expect to display chat one at a time. When I click the Chat button 2 and the Chat 1 is displayed, Chat 1 should be hidden.
Essentially, you need to give each Chat component an identifier and keep track of the one that is currently opened.
Here is the basic structure for your Parent component:
class App extends React.Component {
state = {
currentChatId: null
};
handleOpen = id => {
this.setState({
currentChatId: id
});
};
render() {
return (
<div>
<Chat
identifier={1}
currentChatId={this.state.currentChatId}
handleOpen={this.handleOpen}
/>
<Chat
identifier={2}
currentChatId={this.state.currentChatId}
handleOpen={this.handleOpen}
/>
</div>
);
}
}
So notice, we give each Chat component an identifier prop. We will use identifier to update the active chat - which we stored as a value called currentChatId in our parent-state. That is all done through the handleOpen() event-handler, which we also pass down as a prop to Chat.
Now in your Chat component, we need to configure logic for open() and componentDidUpdate()
class Chat extends React.Component {
constructor() {
super();
this.state = {
show: false
};
}
componentDidUpdate(prevProps) {
const { identifier, currentChatId } = this.props;
if (this.props.currentChatId !== prevProps.currentChatId) {
this.setState({
show: identifier === currentChatId ? true : false
});
}
}
open = () => {
const { identifier, handleOpen } = this.props;
handleOpen(identifier);
};
render() {
return (
<div className="chat">
<button className="btn-yes round" onClick={this.open}>
{this.props.title}
</button>
{this.state.show && (
<div className="show-chat">
<div className="chat-head">
Now Chatting{" "}
<i className="fas fa-angle-down" onClick={this.close} />
</div>
<div className="chat-body">
<div className="blue">
Teresa wants to chat about her healthcare finances
</div>
<ul>
<li>
<img src={""} alt="chat agent avatar" />
</li>
<li>
<h6>John Newman</h6>
<div className="gray">Hi Teresa!</div>
<div className="gray">
Here is the link to the finance tool we
discussed.
</div>
<div className="gray">
If you have any questions, let me know!
</div>
</li>
</ul>
</div>
<input placeholder="Type here and hit enter to chat" />
</div>
)}
</div>
);
}
}
Workflow:
User clicks one of the Chat buttons, triggering handleOpen()and we
pass in the unique identifier....
That gets passed back up to the Parent, and now currentChatId
should be updated with the identifier...
That currentChatId gets passed back down to the Chat component as the
currentChatId prop...
Triggers componentDidUpdate() on all Chat components, and we check
the currentChatId against their own identifiers, only one will be
matching, so we display that one.
See codesandbox for working example: https://codesandbox.io/s/react-example-kgm2h
Im attempting to learn a little bit of react and struggling with this simple concept. I know its just a syntax thing that I'm unfamiliar with but I just can't get it down.
Im attempting to create a tic tac toe game, where each square has a value. When the square is clicked, it should pop up an alert box that shows "You've clicked box {Box Number}". I cannot figure out how to pass the value to the alert function.
class Square extends React.Component {
render() {
return (
<button className="square" onClick={function() {alert("Youve clicked box " + this.props.value);}}>
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
class Game extends React.Component {
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
Looking at your code and based on your comment, the issue is that you're trying to access the props attribute of an object (this) that doesn't have the props attribute.
One way you can do this is to use an arrow function.
<button onClick={() => { alert(`Something ${this.props.value}`); }>Click Me</button>
Another way is to bind the function.
class Box extends React.Component {
constructor(props) {
super(props)
this.onClick = this.onClick.bind(this)
}
onClick() {
alert()
}
render() {
return (
<button onClick={this.onClick}>Click Me</button>
)
}
}
Alternatively, you can directly use an arrow function inside your class so you don't have to bind if you don't want to define one in the render function.
class Box extends React.Component {
onClick = () => {
alert()
}
render() {
return (
<button onClick={this.onClick}>Click Me</button>
)
}
}
Demo: https://jsfiddle.net/1wfyq68r/
You're re-binding the context of this by using a function declaration in your onClick. When you refer to this inside of that function, you're now referring to the context of that function, not the Square class (where this.props would reside).
Any easy fix is to use an arrow function instead–it does not rebind the context of this.
<button
className="square"
onClick={() => alert("Youve clicked box " + this.props.value)}
>
{this.props.value}
</button>
You need to set the context of the click handler function so it references the correct this. This can be done by either
Using an arrow function
() => alert("Youve clicked box " + this.props.value)
Explicitly binding
function() {
alert("Youve clicked box " + this.props.value);
}.bind(this)
A useful resource as to why this is required here
I am trying to make a modal component which I can reuse, but I don't get what I am doing wrong here. The Modal is not appearing. Can anyone help me out?
Little explanation about my app.
This app is loading a JSON url and shows a list of products, which can be marked as done. If you click the div plaatjediv you should get a popup (the modal) with details info over the clicked product.
EDIT: Edited the code as suggested here. I can see the state change to true and false if I click the div, but the Modal is still not appearing.
my code
App.js
import React from 'react';
import ProductModal from './ProductModal.js';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isLoading: true,
orders: [],
dealtOrders: [],
open: false
}
}
toggleModal() {
this.setState({
open: !this.state.open
});
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/26-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
render() {
this.handleDoneAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
const addToDealtOrders = dealtOrders.concat(itemToMoveAtLast);
this.setState({dealtOrders: addToDealtOrders});
this.setState({orders: prevOrders});
};
this.handleUndoAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let orders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const undoDealtOrder = dealtOrders.splice(itemIndex, 1);
const addToOrders = orders.concat(undoDealtOrder);
this.setState({orders: addToOrders});
this.setState({dealtOrders: dealtOrders});
};
const {isLoading, orders, dealtOrders,open} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<ProductModal open={open} />
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map((order, index) => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv" onClick={this.toggleModal}>
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
</div>
</div>
}) : null
}
</div>
<h2>Mandje</h2>
<div className="panel">
{
!isLoading && dealtOrders.length > 0 ? dealtOrders.map((dorder, index) => {
const {productname, image, quantity, orderid} = dorder;
return<div className="productDone" key={index}>
<div className="plaatjediv">
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleUndoAction}>Undo</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
} export default App;
ProductModal.js
import React from 'react';
class ProductModal extends React.Component {
constructor() {
super();
}
render() {
const open = this.props.open;
return (
<div className={'modal fade'+(open ? '' : 'hide')} tabindex="-1" role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">Ă—</span></button>
<h4 className="modal-title">test</h4>
</div>
<div className="modal-body">
test
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
)
}
}
export default ProductModal;
I am unsure what your issue is from your question but I am guessing your model doesn't open?
When you set state, you need to set it to the opposite of this.state.open
You can do it like this:
toggleModal() {
this.setState({
open: !this.state.open
});
}
I can't see where the modal is supposed to be rendered. You have to add it to render function of your "App" class. like this:
render() {
...
return(
<ProductModal open={true} />
...
):
}
and also, in your toggleModal function, do something like this:
this.setState({ open: !this.state.open});
Hope this solves the issue.
The issue is that you do not have your <ProductModal /> as a component in your <App /> In addition to setting your open state, once shown, it will (or should) never hide because you will not be able to toggle it again using your button, and you also do not have any keybindings within your <ProductModal /> itself.
I would suggest you bind an event listener within <ProductModal /> to
Check is ESC key is pressed
Bind a Cancel/Close button (in addition to a header x button).
Listen for if anywhere outside of your dialog is clicked, dismiss the modal.
You will also need to pass a handler from <App /> down to <ProductModal /> to notify when the modal has been closed.
In your App.js
handleClose() {
this.setState({
open: false
});
}
render() {
return (
...
<ProductModal open={this.state.open} handleClose={this.handleClose.bind(this)} />
)
}
Then in your ProductModal.js
handleClose() {
this.props.handleClose();
}
Observe the following using my sandbox:
https://stackblitz.com/edit/react-98m4cr
You'll see that I've implemented the handleClose event to control the state back up to the parent. In addition, you may want to add listeners as mentioned above, all triggering handleClose in the end; just remember to unbind them in ProductModal.js componentWillUnmount.
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I'm trying to create a dynamic list of views managed in a home component that can be saved and loaded. The save and load functions work fine but I get the following error when calling it in the span's returned from the map function in the Footer.js render method
Uncaught TypeError: Cannot read property 'props' of undefined
How can I access this function call in these elements?
Footer.js
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}
You forgot to use an arrow function for getViewNames.map(), and thus the lexical scope of this is not kept in your code -- which is interesting, considering you did use the correct approach everywhere else.
So just do:
this.props.getViewNames().map(viewName => {
...
});
Also see:
How to access the correct `this` context inside a callback?
The reason you cannot access props inside the map function is because this inside your function refers to the context of the map function, and not React Component. You will need to bind your map function to the React Component context by using bind(this) on the map function or arrow functions
Using bind(this)
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
}.bind(this))}
</div>
);
}
}
using arrow function
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map((viewName) => {
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}