Working with React, in my function setTitleAndBody that you can see below, when I console.log the object result I get the title and body information displayed in the developer pannel but the setState is not working and I can't find out why. Maybe something is interacting with it so it cannot be displayed in my input sections but I can't seem to find it.
import React, { Component } from 'react';
import firebase from 'firebase/app';
class TextEditor extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
noteTitre: '',
noteCorps: ''
};
this.handleTitleChange = this.handleTitleChange.bind(this);
this.handleBodyChange = this.handleBodyChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.clearInput = this.clearInput.bind(this);
this.setTitleAndBody = this.setTitleAndBody.bind(this);
}
handleTitleChange(event) {
this.setState({noteTitre: event.target.value});
}
handleBodyChange(event) {
this.setState({noteCorps: event.target.value});
console.log(this.state)
}
handleSubmit(event) {
let postData = {
title: this.state.noteTitre,
body: this.state.noteCorps
}
var dbRef = firebase.database().ref();
dbRef.push(postData);
this.setState({noteTitre: ""});
this.setState({noteCorps: ""});
alert("Note sauvegardée!")
}
clearInput() {
this.setState({noteTitre: ""});
this.setState({noteCorps: ""});
}
setTitleAndBody(result) {
console.log(result)
this.setState({noteTitre: result.title});
this.setState({noteCorps: result.body});
}
render() {
return (
<div className="writing">
<div className="form-group">
{/* Titre */}
<input
name="noteTitre"
className="form-control"
placeholder="Titre de la note"
value={this.state.noteTitre}
onChange={this.handleTitleChange} />
<br/>
{/* Corps */}
<textarea
name="noteCorps"
className="form-control"
placeholder="Corps de la note"
value={this.state.noteCorps}
onChange={this.handleBodyChange} />
</div>
<div className="text-right">
{/* Bouton d'annulation */}
<button
onClick={this.clearInput}
className="btn btn-danger mr-2 rounded">
Annuler
</button>
{/* Bouton de sauvegarde */}
<button
onClick={this.handleSubmit}
className="btn btn-success ml-2 rounded"
id="bouton_sauvegarde">
Sauvegarder
</button>
</div>
</div>
);
}
}
export default TextEditor;
destructuring title and body will solve the problem,
better approach:
setTitleAndBody(result) {
const {title, body} = result;
this.setState({...this.state, noteTitre:title, noteCorps:body });
}
follow above approach every where you are setting a state such as:
handleTitleChange(event) {
this.setState({...this.state, noteTitre: event.target.value});
}
handleBodyChange(event) {
this.setState({...this.state, noteCorps: event.target.value});
}
``
I did not see where you use setTitleAndBody in this code.
And how do you know that state is not change?
How you test it?
if you decide that is not changed because printing with console.log(this.state) it is not gonna work, because setState is asynchronous.
you can provide the console.log as callback to setState. setState(updater, callback) to see the right value.
setTitleAndBody(result) { this.setState({noteTitre: result.title}, () => console.log(this.state)); ........ }
Related
I am making a dashboard component which displays rendered previews and code for HTML snippets. Inside of the dashboard component I am mapping the array of snippets using .map. Each mapped snippet is going to have a delete function (already built) and an update function.
For the update function to work each snippet has it's own child modal component. I need to pass the ID of the snippet to the modal component where I can combine the ID with the new content before updating the database and state.
However, I'm making a mistake somewhere as I pass the ID as props to the modal.
.map used inside of my Dashboard.js Dashboard class component.
{this.state.snippets.map(snippet => (
<>
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p> //THIS WORKS
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button type="button" onClick={this.showModal}>
Open
</button>
</div>
</div>
</div>
<Modal
sid={snippet._id} //PASS ID HERE
show={this.state.show}
handleClose={this.hideModal}
></Modal>
</>
))}
This renders the snippets below (3 snippet pods, with their database ID included).
The open button opens the modal (Modal.js) below.
import React, { Component } from 'react'
import api from '../api'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value,
})
}
handleClick = id => event => {
event.preventDefault()
console.log(id)
}
render() {
const { sid, show, handleClose } = this.props
console.log(sid)
const showHideClassName = show ? 'modal display-flex' : 'modal display-none'
return (
<div id="Modal" className={showHideClassName}>
<div id="modal-main">
<h4>Edit snippet {sid}</h4>
<form>
Name:{' '}
<input
type="text"
value={this.state.name}
name="name"
onChange={this.handleInputChange}
/>{' '}
<br />
Details:{' '}
<input
type="text"
value={this.state.details}
name="details"
onChange={this.handleInputChange}
/>{' '}
<br />
Content:{' '}
<textarea
value={this.state.content}
name="content"
cols="30"
rows="10"
onChange={this.handleInputChange}
/>{' '}
<br />
<button onClick={this.handleClick(sid)}>TEST ME</button>
</form>
<button onClick={handleClose}>Close</button>
{this.state.message && (
<div className="info">{this.state.message}</div>
)}
</div>
</div>
)
}
}
The console.log just under the render actually pastes the correct 3 ID's the console.
However, calling the ID (sid) within the Modal.js return will only show the last snippet ID, no matter which Modal I open. The same goes for pushing that ID to the handleClick function where I intend to combine the ID with an update package.
Solution below as initiated by HMR in the comments.
The problem was all the modals were showing and just the last one was visible.
Fixed by moving the modal out of the .map and instead updating the ID from within the .map to the state and passing the state ID to a new nested component within the modal.
Also switched to using dynamic CSS to show and hide the modal based on the state.
Dashboard.jsx
export default class Snippets extends Component {
constructor(props) {
super(props)
this.showModal = React.createRef()
this.state = {
snippets: [],
show: false,
sid: '',
}
}
handleDelete = id => event => {
event.preventDefault()
api
.deleteSnippet(id)
.then(result => {
console.log('DATA DELETED')
api.getSnippets().then(result => {
this.setState({ snippets: result })
console.log('CLIENT UPDATED')
})
})
.catch(err => this.setState({ message: err.toString() }))
}
handleModal = id => {
this.setState({ sid: id })
this.showModal.current.showModal()
}
//<div id="preview">{ReactHtmlParser(snippet.content)}</div>
render() {
return (
<>
<Modal ref={this.showModal} handleClose={this.hideModal}>
<ModalUpdate sid={this.state.sid} />
</Modal>
<div className="Dashboard">
<div className="wrapper">
<div className="container">
<div className="holder">
<div className="content">
<div className="content__body">
<h3>Dashboard</h3>
</div>
</div>
</div>
<div className="break"></div>
{this.state.snippets.map(snippet => (
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p>
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button
type="button"
onClick={() => this.handleModal(snippet._id)}
>
Open
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
Modal.jsx
import React, { Component } from 'react'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
showModal = () => {
this.setState({ show: true })
}
hideModal = () => {
this.setState({ show: false })
}
render() {
return (
<div
id="Modal"
style={{ display: this.state.show === true ? 'flex' : 'none' }}
>
<div id="modal-main">
<h4>Edit snippet </h4>
{this.props.children}
<button onClick={() => this.hideModal()}>Close</button>
</div>
</div>
)
}
}
ModalUpdate.jsx
import React, { Component } from 'react'
export default class ModalUpdate extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
// handleInputChange = event => {
// this.setState({
// [event.target.name]: event.target.value,
// })
// }
// handleClick = id => event => {
// event.preventDefault()
// console.log(id)
// }
render() {
return <h4>ID = {this.props.sid}</h4>
}
}
I am not sure about the handleDelete function,. but replacing the line should solve the issue probably
<button onClick={() => this.handleDelete(snippet._id)}>
One potential issue is the this.handleDelete(snippet._id) will fire immediately rather than onClick, so you will need to add an anonymous function in the event listener:
() => this.handleDelete(snippet._id)
instead of
this.handleDelete(snippet._id)
After typing in a team name, I want react to redirect us to the specified page (ie: "teams/this.state.searchText" w/ search text being what the user has typed into the search form). I get a re-render that does nothing/does no redirecting... Can this be done with reacts new v4 Redirect component?
export default class Nav extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
searchText: ''
}
this.submit = this.submit.bind(this);
}
onSearchChange = e => {
console.log(e.target.value)
this.setState({ searchText: e.target.value });
}
submit = e => {
e.preventDefault();
// with the new search state from above, get the state and perform a search with it to local/team/"searchValue"
e.currentTarget.reset();
}
redirectIt = () => {
this.props.history.push(`teams/${this.state.searchText}`)
}
render() {
return (
<Navbar className="bg-light justify-content-between">
<Form onSubmit={this.submit} >
<FormControl type="text" placeholder="Search Team" className=" mr-sm-2" onChange={this.onSearchChange} />
<Button type="submit">Submit</Button>
</Form >
<div className='logo'>NHL</div>
<Form inline>
<Button type="submit" onClick={this.redirectIt}>Login</Button>
</Form>
</Navbar>
);
}
}
With Redirect, it would look something like this. you could basically tell the browser to go to a different page
import { Redirect } from 'react-router-dom'
export default class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
isRedirected: false
}
}
onSearchChange = e => {
console.log(e.target.value)
this.setState({ searchText: e.target.value });
}
submit = e => {
e.preventDefault();
// with the new search state from above, get the state and perform a search with it to local/team/"searchValue"
e.currentTarget.reset();
}
redirectIt = () => {
this.setState({isRedirected: true})
}
render() {
// change the to prop to the next component
if (this.state.isRedirected) return <Redirect to=`/teams/${this.state.searchText}` />
return (
<Navbar className="bg-light justify-content-between">
<Form onSubmit={this.submit}>
<FormControl type="text" placeholder="Search Team" className=" mr-sm-2" onChange={this.onSearchChange} />
<Button type="submit">Submit</Button>
</Form >
<div className='logo'>NHL</div>
<Button onClick={this.redirectIt}>Login</Button>
</Navbar>
);
}
This question already has answers here:
Unable to access React instance (this) inside event handler [duplicate]
(19 answers)
Closed 3 years ago.
I am trying to update state value but in meantime it give me an error that TypeError: Cannot read property 'state' of undefined. when I type something in field then click on submit then I got this error . I think it might be silly question to ask but trust me I am new to React . Could someone please help me how to solve this problem , I really need to solve my problem .
Thanks
Code
//ModalComponent.js
import React from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
import axios from "axios";
export default class ModalComponent extends React.Component {
constructor(props) {
super(props);
this.state = { modal: false, subject: "", taskType: "", notes: "" };
}
toggle = () => {
this.setState({
modal: !this.state.modal
});
};
handleSubject = e => {
this.setState({ subject: e.target.value });
};
handleTaskType = e => {
this.setState({ taskType: e.target.value });
};
handleNotes = e => {
this.setState({ country: e.target.value });
};
handleSubmit(e) {
e.preventDefault();
const form = {
subject: this.state.subject,
taskType: this.state.taskType,
notes: this.state.notes
};
let uri = "localhost:3000/api/diary/update";
axios.post(uri, form, { params: { id: 347 } }).then(response => {
this.setState({
modal: !this.state.modal
});
});
}
render() {
console.log(this.state.subject);
return (
<div>
<h1>React Bootstrap Modal Example</h1>
<Button color="success" onClick={this.toggle}>
React Modal
</Button>
<Modal isOpen={this.state.modal}>
<form onSubmit={this.handleSubmit}>
<ModalHeader>IPL 2018</ModalHeader>
<ModalBody>
<div className="row">
<div className="form-group col-md-4">
<label>Subject:</label>
<input
type="text"
name="subject"
value={this.state.subject}
onChange={this.handleSubject}
className="form-control"
/>
</div>
</div>
<div className="row">
<div className="form-group col-md-4">
<select onChange={this.handleTaskType}>
<option>Select Task Type</option>
<option value="meeting">Meeting</option>
<option value="followUp">Follow Up</option>
<option value="reminder">Reminder</option>
<option value="other">Other</option>
</select>
</div>
</div>
<div className="row">
<div className="form-group col-md-4">
<label>Country:</label>
<input
type="text"
value={this.notes}
onChange={this.handleNotes}
className="form-control"
/>
</div>
</div>
</ModalBody>
<ModalFooter>
<input
type="submit"
value="Submit"
color="primary"
className="btn btn-primary"
/>
<Button color="danger" onClick={this.toggle}>
Cancel
</Button>
</ModalFooter>
</form>
</Modal>
</div>
);
}
}
You have to tell your handleSubmit method which this you want to use.
As the docs say :
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
To do that, you can bind handleSubmit in your constructor like this :
constructor(props) {
...
this.handleSubmit = this.handleSubmit.bind(this);
...
}
setState is asynchronous so there are two options to fix this issue, one is to use arrow function in handleSubmit and the second one is to bind the function.
Code below should solve your problem.
handleSubmit = e => {
e.preventDefault();
const form = {
subject: this.state.subject,
taskType: this.state.taskType,
notes: this.state.notes
};
let uri = "localhost:3000/api/diary/update";
axios.post(uri, form, { params: { id: 347 } }).then(response => {
this.setState({
modal: !this.state.modal
});
});
}
Try This,
constructor(props) {
super(props);
this.state = { modal: false, subject: "", taskType: "", notes: "" };
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSubject = this.handleSubject.bind(this);
this.handleTaskType = this.handleTaskType.bind(this);
this.handleNotes = this.handleNotes.bind(this);
}
I have an application with many items collection, and in admin area, the admin user can delete or modify items (like a traditional e-commerce website back office). In logic, for modify an item, user click on "modify item" button, and a modal open.
My problem is i can't open modal on click button, because my browser show me an error message when I click on "modify item" button : "TypeError: Cannot read property 'setState' of undefined"
I use react-modal-responsive for this task.
I have been stuck for several days, I would be super happy if you could help me. Thank in advance.
This is my code:
import React, { Component } from 'react';
import { database } from '../firebase/firebase';
import * as firebase from 'firebase';
import withAuthorization from './withAuthorization';
import _ from 'lodash';
import AuthUserContext from './AuthUserContext';
import FileUploader from "react-firebase-file-uploader";
import Image from 'react-image-resizer';
import{InstantSearch, SearchBox, Hits, Highlight, RefinementList} from "react-instantsearch/dom";
import Modal from 'react-responsive-modal';
function removeToCatalogue(hit) {
const item = hit.objectID;
firebase.database().ref(`catalogue/${item}`).remove();
console.log("Capsule correctement supprimée du catalogue")
}
const Hit = ({hit}) =>
<div className="item">
<img src={hit.avatarURL} width={150} height={150}></img>
<h1 className="marque">{hit.marque}</h1>
<h3 className="numero">{hit.numero}</h3>
<h4 className="reference">{hit.reference}</h4>
<h4 className="marquesuite">{hit.marquesuite}</h4>
<p className="cote">{hit.cote}</p>
<button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button>
<button onClick={() => this.setState({open: true})}>Modify Item</button>
</div>
const Content = () =>
<div className="text-center">
<Hits hitComponent={Hit}/>
</div>
class Admin extends React.Component {
constructor (props) {
super (props);
this.state = {
marque: '',
marquesuite: '',
numero: '',
reference: '',
cote: '',
avatar: "",
isUploading: false,
progress: 0,
avatarURL: "",
catalogue: {},
open: false,
};
this.onInputChange = this.onInputChange.bind(this);
this.onHandleSubmit = this.onHandleSubmit.bind(this);
}
onOpenModal = () => {
this.setState({ open: true });
};
onCloseModal = () => {
this.setState({ open: false });
};
onInputChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
onHandleSubmit(e){
e.preventDefault();
const catalogue = {
marque: this.state.marque,
marquesuite: this.state.marquesuite,
numero: this.state.numero,
reference: this.state.reference,
cote: this.state.cote,
avatar: this.state.avatar,
avatarURL: this.state.avatarURL,
};
database.push(catalogue);
this.setState({
marque: '',
marquesuite: '',
numero: '',
reference: '',
cote: '',
avatar: "",
isUploading: false,
progress: 0,
avatarURL: "",
});
}
handleUploadStart = () => this.setState({ isUploading: true, progress: 0 });
handleProgress = progress => this.setState({ progress });
handleUploadError = error => {
this.setState({ isUploading: false });
console.error(error);
};
handleUploadSuccess = filename => {
this.setState({ avatar: filename, progress: 100, isUploading: false });
firebase
.storage()
.ref("images")
.child(filename)
.getDownloadURL()
.then(url => this.setState({ avatarURL: url }));
};
render (){
const { open } = this.state;
return (
<div className="container-fluid">
<div className="container">
<h1 class="text-center">Espace d'Administration</h1>
Signaler une modification
<form onSubmit={this.onHandleSubmit}>
<div className="form-group">
<label>Marque de la capsule:</label>
<input
value={this.state.marque}
type="text"
name='marque'
placeholder="Marque"
onChange={this.onInputChange}
ref="marque"
className="form-control" />
</div>
<div>
<label>Numéro de la capsule:</label>
<input
value={this.state.numero}
type="text"
name='numero'
placeholder="Numéro de la capsule"
onChange={this.onInputChange}
ref="numero"
className="form-control"/>
</div>
<div className="form-group">
<label>Référence de la capsule:</label>
<input
value={this.state.marquesuite}
type="text"
name='marquesuite'
placeholder="Référence de la capsule"
onChange={this.onInputChange}
ref="marquesuite"
className="form-control"/>
</div>
<div className="form-group">
<label>Référence de la capsule (suite):</label>
<input
value={this.state.reference}
type="text"
name='reference'
placeholder="Référence de la capsule (suite)"
onChange={this.onInputChange}
ref="reference"
className="form-control"/>
</div>
<div className="form-group">
<label>Cote de la capsule:</label>
<input
value={this.state.cote}
type="text"
name='cote'
placeholder="Cote de la capsule"
onChange={this.onInputChange}
ref="cote"
className="form-control"/>
</div>
<label>Visuel de la capsule:</label>
{this.state.isUploading && <p>Progress: {this.state.progress}</p>}
{this.state.avatarURL && <img src={this.state.avatarURL} />}
<FileUploader
accept="image/*"
name="avatar"
randomizeFilename
storageRef={firebase.storage().ref("images")}
onUploadStart={this.handleUploadStart}
onUploadError={this.handleUploadError}
onUploadSuccess={this.handleUploadSuccess}
onProgress={this.handleProgress}
/>
<button className="btn btn-info">Ajouter une capsule</button>
</form>
</div>
<h1 className="text-center">Catalogue de capsule</h1>
<InstantSearch
apiKey="xxx"
appId="xxx"
indexName="xxx">
<SearchBox translations={{placeholder:'Rechercher une capsule'}} width="500 px"/>
<div>
<Modal open={this.state.showModal} open={open} onClose={this.onCloseModal} center>
<h2>Simple centered modal</h2>
</Modal>
</div>
<Content />
</InstantSearch>
</div>
)
}
}
const authCondition = (authUser) => !!authUser;
export default withAuthorization(authCondition)(Admin);
I don't know if this answer might entirely solve your problem, but it'll at least let you open the modal.
First, you need to pass an handler when rendering the Content component, something like:
<Content onEdit={ this.onOpenModal } />
Then, we should have a similar handler on the Hit component, attaching it to the button click event:
const Hit = ({ hit, onEdit }) =>
<div className="item">
<img src={hit.avatarURL} width={150} height={150}></img>
<h1 className="marque">{hit.marque}</h1>
<h3 className="numero">{hit.numero}</h3>
<h4 className="reference">{hit.reference}</h4>
<h4 className="marquesuite">{hit.marquesuite}</h4>
<p className="cote">{hit.cote}</p>
<button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button>
<button onClick={ onEdit }>Modify Item</button>
</div>
Now we have to pass down this handler from the Content component to the Hit one.
The problem I see is that the Hits component take a hitComponent prop which has to be a component to be rendered. This means that, to pass the onEdit handler, we have to enhance the Hit component with a Javascript clojure before passing it down:
const Content = ({ onEdit }) => {
const EnhancedHit = props =>
<Hit onEdit={ onEdit } { ...props } />
return (
<div className="text-center">
<Hits hitComponent={ EnhancedHit } />
</div>
)
}
Can you try and report if it opens the modal?
A component's state is enclosed within the component that defines it. In your case you are trying to update the state of the Admin through Hit component. From React.js State and Life Documentation
State is similar to props, but it is private and fully controlled by
the component.
But the reason this error is thrown is not because Hit can't update the state of Admin, it doesn't event try to. What happens when you click the Modify Item button is Hit is trying to update its own state, but this won't work for two reasons:
First and foremost from the same documentation:
We mentioned before that components defined as classes have some additional features. Local state is exactly that: a feature available only to classes.
Even if you transform the component to a class component, the state should be defined either in the constructor or as class field. But this won't help you anyway, because although you will be able to update the state, it would be Hit's one.
So, if I were you, you would go for one of the following solutions (depending on the case).
Inline Content, Hits and Hit within Admin's render method or extract them as renderContent and renderHits Admin's class field methods, this way you will be able to update the state within this methods methods.
Probably the one you should consider more: Pass openModel class field function as onModifyItem prop down to Hits component, and use it as onClick prop for Modify Item button.
Here is an example:
const Hit = ({onModifyItem, hit}) =>
<div className="item">
<img src={hit.avatarURL} width={150} height={150}></img>
<h1 className="marque">{hit.marque}</h1>
<h3 className="numero">{hit.numero}</h3>
<h4 className="reference">{hit.reference}</h4>
<h4 className="marquesuite">{hit.marquesuite}</h4>
<p className="cote">{hit.cote}</p>
<button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button>
<button onClick={onModifyItem}>Modify Item</button>
</div>
const Content = ({ onModifyItem }) => {
const HitComponent = props => <Hit onModifyItem={onModifyItem} {...props}/>
return (<div className="text-center">
<Hits hitComponent={HitComponent}/>
</div>);
}
<Content onModifyItem={this.openDialog}/>
I'm using React and firebase to create a simplified slack and using MDl for styles. I'm trying to integrate a button that opens up a dialog box to get some user input, in this case the name of a new chat room, and then when submitted it will send the data to firebase and store it in the rooms array and display the new room name in the list. I have already set up the form to get user input and then I tried to refactor it to work in a dialog box and I seem stuck on how to get the dialog box to work. Here is my whole component:
import React, { Component } from 'react';
import './RoomList.css';
import dialogPolyfill from 'dialog-polyfill';
class RoomList extends Component {
constructor(props) {
super(props);
this.state = { rooms: [] };
this.roomsRef = this.props.firebase.database().ref('rooms');
this.handleChange = this.handleChange.bind(this);
this.createRoom = this.createRoom.bind(this);
}
handleChange(e){
this.setState({ name: e.target.value });
}
createRoom(e) {
e.preventDefault();
this.roomsRef.push({ name: this.state.name });
this.setState({ name: "" });
}
componentDidMount() {
this.roomsRef.on('child_added', snapshot => {
const room = snapshot.val();
room.key = snapshot.key;
this.setState({ rooms: this.state.rooms.concat( room ) });
})
}
dialogBox(e){
const dialogButton = document.getElementsByClassName('dialog-
button');
const dialog = document.getElementById('dialog');
if (! dialog.showModal) {
dialogPolyfill.registerDialog(dialog);
}
dialogButton.onClick( (e) => {
dialog.showModal();
});
dialog.document.getElementsByClassName('submit-close').onCLick(
(e) => {
dialog.close();
});
}
render() {
const roomlist = this.state.rooms.map( (room) =>
<span className="mdl-navigation__link" key={room.key}>
{room.name}</span>
);
const newListForm = (
<div id="form">
<button className="mdl-button mdl-js-button mdl-button--
raised mdl-js-ripple-effect dialog-button">Add room</button>
<dialog id="dialog" className="mdl-dialog">
<h3 className="mdl-dialog__title">Create Room name</h3>
<div className="mdl-dialog__content">
<p>Enter a room name</p>
</div>
<div className="mdl-dialog__actions">
<form onSubmit={this.createRoom}>
<div className="mdl-textfield mdl-js-textfield">
<input type="text" value={this.state.name}
className="mdl-textfield__input" id="rooms" onChange=
{this.handleChange} />
<label className="mdl-textfield__label"
htmlFor="rooms">Enter room Name...</label>
<button type="submit" className="mdl-button submit-
close">Submit</button>
<button type="button" className="mdl-button submit-
close">Close</button>
</div>
</form>
</div>
</dialog>
</div>
);
return (
<div className="layout mdl-layout mdl-js-layout mdl-layout--
fixed-drawer mdl-layout--fixed-header">
<header className="header mdl-layout__header mdl-color--
grey-100 mdl-color-text--grey-600">
<div className="mdl-layout__header-row">
<span className="mdl-layout-title">Bloc Chat</span>
<div className="mdl-layout-spacer"></div>
</div>
</header>
<div className="drawer mdl-layout__drawer mdl-color--blue-
grey-900 mdl-color-text--blue-grey-50">
<header className="drawer-header">
<span>{newListForm}</span>
</header>
<nav className="navigation mdl-navigation mdl-color--
blue-grey-800">
<div>{roomlist}</div>
<div className="mdl-layout-spacer"></div>
</nav>
</div>
</div>
);
}
}
export default RoomList;
Here is a simple sample on how to build a modal with the new portal API provided from React v16.xx
Working demo can be found here. Just use the dropdown to navigate to the simple portal demo. A snapshot of the full code base can be found on github.
Working Code
import React, { Component } from "react";
import { createPortal } from "react-dom";
import "./simple-portal.css";
export default class SimplePortal extends Component {
constructor() {
super();
this.state = {
list: [],
input: "",
showDialog: false
};
this._onChange = this._onChange.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
_onChange(e) {
let input = e.target.value;
this.setState({ input });
}
_onSubmit(e) {
e.preventDefault();
let showDialog = false;
// Dont Mutate the State!!!
let list = this.state.list.slice();
list.push(this.state.input);
this.setState({ showDialog, list, input: "" });
}
render() {
const { showDialog, list, input } = this.state;
return (
<div className="container">
<div>
<button
className="btn"
onClick={e =>
this.setState({
showDialog: !showDialog
})
}
>
Add Item
</button>
</div>
{/* Render Items from List */}
<div>
<ul>
{list.map(item => {
return <li key={item}>{item}</li>;
})}
</ul>
</div>
{/* Show Modal - Renders Outside React Hierarchy Tree via Portal Pattern */}
{showDialog === true ? (
<DialogModal>
<div className="dialog-wrapper">
<h1>New List Item</h1>
<form onSubmit={this._onSubmit}>
<input type="text" value={input} onChange={this._onChange} />
</form>
</div>
</DialogModal>
) : null}
</div>
);
}
}
class DialogModal extends Component {
constructor() {
super();
this.body = document.getElementsByTagName("body")[0];
this.el = document.createElement("div");
this.el.id = "dialog-root";
}
componentDidMount() {
this.body.appendChild(this.el);
}
componentWillUnmount() {
this.body.removeChild(this.el);
}
render() {
return createPortal(this.props.children, this.el);
}
}
I don't see any event listeners on your buttons that would trigger a rendering of the modal. I would approach this by specifying an onClick event that would update the state which would render the modal/dialogue box.
Another solution, which may be the way you are thinking, is to have the state change to render the modal/dialogue box as visible from within the createRoom() function. Remember, updating state or getting new props will trigger a rendering of the component. You are trying to update the state to re-render your component with the modal/dialogue being shown.
Sorry if I misunderstood the question or your goal.