Modal isn't opening as expected - reactjs

class Posts extends Component {
constructor(){
super();
this.state = {
modalIsOpen:false
};
this.openModal = this.openModal.bind(this);
this.afterOpenModal = this.afterOpenModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
console.log("got here?")
this.setState({modalIsOpen: true});
}
afterOpenModal() {
// references are now sync'd and can be accessed.
this.subtitle.style.color = '#f00';
}
closeModal() {
this.setState({modalIsOpen: false});
}
render() {
const keys = generateKey(new Date().getTime())
var dictionary = this.props.posts
const postItemsArr = Object.keys(dictionary).map(post=>dictionary[post])
const number = 0
const postItems = postItemsArr.map(
post=>(
<Jumbotron key={generateKey(post.positiontitle) + generateKey(post.businessId)} >
<div className="position">{post.positiontitle}</div><br></br>
<BusinessName businessnameType={post.businessname} /><br></br>
<JobDescription jobDescription={post.description_sanitized} /><br></br>
<p>
<Modal isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
style={customStyles}
contentLabel="Example"
>
<h2 ref={subtitle => this.subtitle = subtitle}>Hello</h2>
<button onClick={this.closeModal}>close</button>
<div>I am a modal</div>
<form>
<input />
<button>tab navigation</button>
<button>stays</button>
<button>inside</button>
<button>the modal</button>
</form>
</Modal>
<button onClick={this.openModal}>Open Modal</button>
</p>
</Jumbotron>
)
)
return (
<div>
<h1> Jobs Listings </h1>
{postItems }
</div>
);
}
}
Inside my Jumbotron, my modal doesn't seem to be opening, why?
It looks like it gets to the state openModal()
but doesn't actually open the modal when the user clicks on
<button onClick={this.openModal}>Open Modal</button>
Also; should I create a separate component called Modal; what would be best practices?
Overall I'm trying to trigger the modal object for a respective list item in the list.

I would create a new presentational component called modal, then use that when you need it, rather than do what you're trying to do now, which is pass an open/close state. The Modal can be a simple presentational component that just accepts props. By doing it this way, you create reusability.
function Modal(props) {
return (
<div className={props.css.awesomeLayout}>
{props.aProp}
</div>
)
}

Related

React How to return div and variable value for another component

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>
)
}
}

Pass a react value into a JS Alert

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 pass a value from one component with a click function to another

My app.js creates a widget layout, within it I have an handleWidgetSelection function which should take an id when an item is clicked in my modal component. When I console log in the handleWidgetSelection function I notice that the state is not updated and it remains null as I originally set it as. I am fairly new to react so any help would be great.
This is my app.js
class App extends Component {
constructor(props) {
super(props);
this.state={
selectedWidgetId: null, //initially set as null because no widget is selected
widgetOptions:[{name:"Data Table", comp:<DataTable/>},{name:"List", comp:<CheckboxList/>}],
widgets:[ //array for layout
{id:1, content: <DataTable/>},
{id:2, content: <CheckboxList/>},
{id:3, content: ""},
{id:4, content: ""}
],
isModalOpen: false
}
}
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id})
console.log(this.state.selectedWidgetId); //trying to fix so this value does not always print null
}
.....
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
//I am passing my values to the AddWidgetDialog component here
<AddWidgetDialog handleWidgetSelection={this.handleWidgetSelection} widgets={this.state.widgetOptions} isModalOpen={this.state.isModalOpen} onRequestClose={this.onRequestClose} />
<Grid container spacing={24}>
{
this.state.widgets.map((widget,index)=>{
return(
<Grid item xs={12} sm={6}>
<Paper className={classes.paper}><Swappable id={widget.id} content={widget.content} delete={this.deleteEvent.bind(this,index)} add={this.addEvent.bind(this,index)}/></Paper>
</Grid>
)
})
}
</Grid>
</div>
);
}
This is my AddWidgetDialog component
import React, { PropTypes } from 'react';
import Modal from 'react-modal';
const AddWidgetDialog = ({ handleWidgetSelection, widgets, isModalOpen, onRequestClose}) => {
const widgetItems = widgets.map((widget) => {
return (
<div className="list-group">
<a href="#" onClick={() => handleWidgetSelection(widget.name)} className="list-group-item">
<h6 className="list-group-item-heading">{widget.name}</h6>
</a>
</div>
);
});
return (
<Modal
className="Modal__Bootstrap modal-dialog"
isOpen={isModalOpen}>
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" onClick={onRequestClose}>
<span aria-hidden="true">×</span>
<span className="sr-only">Close</span>
</button>
<h4 className="modal-title">Add a widget</h4>
</div>
<div className="modal-body">
<h5>Pick a widget to add</h5>
{widgetItems}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" onClick={onRequestClose}>Close</button>
</div>
</div>
</Modal>
);
};
export default AddWidgetDialog;
setState maybe asynchronous. See the docs Why is setState giving me the wrong value?
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id})
console.log(this.state.selectedWidgetId); //no guarantee state is updated or not.
}
You should use console.log() in the callback as second argument to the setState
handleWidgetSelection=(id) => {
this.setState({selectedWidgetId: id},() => {
console.log(this.state.selectedWidgetId); //state is updated now.
})
}

Reactjs component modal onclick div

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.

How to make 'this' refer to App, not modal

In my EditRecipeForm component, when editing text in the modal, I get Uncaught TypeError: this.setState is not a function , because this refers to the modal window, and not the "app", the thing I don't understand is how to get this to be correct, so that editing the field value updates actually works.
(I removed code not directly related), full demo available here: http://codepen.io/pdewouters/pen/adLpvQ
const Button = ReactBootstrap.Button,
Accordion = ReactBootstrap.Accordion,
Panel = ReactBootstrap.Panel,
Modal = ReactBootstrap.Modal,
Input = ReactBootstrap.Input,
ButtonToolbar = ReactBootstrap.ButtonToolbar,
ButtonInput = ReactBootstrap.ButtonInput,
ListGroup = ReactBootstrap.ListGroup,
ListGroupItem = ReactBootstrap.ListGroupItem
class App extends React.Component {
constructor(props){
super(props)
this.state = {
showModal: false,
showEditModal: false,
recipes: [],
recipeName: '',
ingredients: '',
editRecipeNameInputVal: '',
editIngredientsInputVal: '',
recipeToEdit: {recipeName:'',ingredients:''}
}
}
closeEditModal(){
this.setState({ showEditModal: false })
}
openEditModal(recipe){
this.setState({showEditModal:true,recipeToEdit:recipe})
}
handleRecipeEditInputChange(value){
this.setState({editRecipeNameInputVal: value})
}
handleIngredientsEditInputChange(value){
this.setState({editIngredientsNameInputVal: value})
}
render(){
return(
<div className="container">
<div className="page-header">
<h1>Recipes</h1>
</div>
<div className="row">
<div className="col-md-12">
<EditRecipeForm
showModal={this.state.showEditModal}
closeModal={this.closeEditModal.bind(this)}
handleSubmit={this.handleEditFormSubmit.bind(this)}
handleRecipeEditInputChange={this.handleRecipeEditInputChange.bind(this)}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange.bind(this)}
recipe={this.state.recipeToEdit}
handleRecipeEditInputChange={this.handleRecipeEditInputChange}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange} />
<RecipeList recipes={this.state.recipes}
handleDeleteRecipe={this.handleDeleteRecipe.bind(this)}
handleOpenEditModal={this.openEditModal.bind(this)}
/>
</div>
</div>
</div>
)
}
}
class RecipeList extends React.Component {
handleOpenEditModal(recipe,event) {
this.props.handleOpenEditModal(recipe)
}
render(){
let recipes = this.props.recipes.map((recipe,index)=>{
return (
<Panel header={recipe.recipeName} eventKey={index}>
<IngredientsList ingredients={recipe.ingredients} />
<ButtonToolbar>
<Button bsStyle="danger" onClick={this.props.handleDeleteRecipe.bind(null,index)}>Delete</Button>
<Button bsStyle="primary" onClick={this.handleOpenEditModal.bind(this,recipe)}>Edit</Button>
</ButtonToolbar>
</Panel>
)
})
return (
<Accordion>
{recipes}
</Accordion>
)
}
}
class EditRecipeForm extends React.Component {
handleRecipeEditInputChange(e){
this.props.handleRecipeEditInputChange(e.target.value)
}
handleIngredientsEditInputChange(e){
this.props.handleIngredientsEditInputChange(e.target.value)
}
render(){
return(
<Modal show={this.props.showModal} onHide={this.props.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Edit recipe</Modal.Title>
</Modal.Header>
<Modal.Body>
<form onSubmit={this.props.handleSubmit}>
<Input type="text"
label="Recipe"
placeholder="blueberry pancakes"
value={this.props.recipe.recipeName}
onChange={this.handleRecipeEditInputChange.bind(this)} />
<Input type="textarea"
label="Ingredients"
placeholder="milk,sugar,flour,butter,blueberries"
value={this.props.recipe.ingredients}
onChange={this.handleIngredientsEditInputChange.bind(this)} />
<ButtonInput type="submit" value="Save changes" />
</form>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.closeModal}>Close</Button>
</Modal.Footer>
</Modal>
)
}
}
React.render(<App />, document.querySelector('.app'))
It looks like you're passing the handleRecipeEditInputChange and handleIngredientsEditInputChange prop callbacks twice and the second time you are not binding to the component instance.
<EditRecipeForm
showModal={this.state.showEditModal}
closeModal={this.closeEditModal.bind(this)}
handleSubmit={this.handleEditFormSubmit.bind(this)}
handleRecipeEditInputChange={this.handleRecipeEditInputChange.bind(this)}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange.bind(this)}
recipe={this.state.recipeToEdit}
handleRecipeEditInputChange={this.handleRecipeEditInputChange}
handleIngredientsEditInputChange={this.handleIngredientsEditInputChange} />
The second props will overwrite the first ones, so try deleting the second ones.

Resources