I have following component.
import React from 'react'
import Profile from './Profile'
import Messages from './Messages'
class ContactContent extends React.Component {
constructor () {
super()
this.state = {
message: ''
}
this.handleOnClick = (e) => {
e.preventDefault()
console.log('send message called')
// this.props.onSendMessage(this.state.message)
}
}
render () {
const { id, name, profile, messages } = this.props.user
return (
<div className='content'>
<Profile
name={id}
profile={profile}
/>
<Messages
messages={messages}
/>
<div className='message-input'>
<div className='wrap'>
<input type='text' placeholder='Write your message...' onChange={(e) => this.setState({ message: e.target.value })} />
{/* <i className="fa fa-paperclip attachment" aria-hidden="true"></i> */}
<button className='' onClick={this.handleOnClick}><i className='fa fa-paper-plane' aria-hidden='true' /></button>
</div>
</div>
</div>
)
}
}
export default ContactContent
when I click on button onclick is called which triggers handleonclick function. but handleonclick is being called like infinit time. this is a weird behavior I have seen this first time any idea what I have done wrong?
Fixed it there was an external javascript file which was creating this issue. Nothing is wrong with the code.
No, you are calling the handleOnClick each time on page load/render load here
So without making this, or to avoid multiple call try to use:
fat arrow call of that function on onClick event.
<button className='' onClick={(e) => this.handleOnClick(e)}><i className='fa fa-paper-plane' aria-hidden='true' /></button>
Related
This is a very noob question but I've been trying all day do implement this. Please help me out.
Sorry for the length, just tried to put out the whole thing I am struggling with
I am trying to build custom buttons and to do so, I created a component so I can create as many buttons that I want. For that I declared a state and passed down some information as props, which is as follows:
import React, {useState} from 'react'
import Button from '../components/Button'
function CustomButton() {
const [clicked, setClicked] = useState(false)
return (
<div className='CustomButton'>
<Navbar />
<Button setClicked={setClicked} name="Button One" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Two" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Three" clicked={clicked}/>
</div>
)
}
export default CustomButton
As you can see, we passed the state and name of that button down. To render this Buttons, following component has been created:
import React from 'react'
import Modal from './Modal/Modal'
function Button({setClicked, name, clicked}) {
return (
<div>
<button onClick={() => {setClicked(true)}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
export default Button
And lastly, when once a button is clicked, we want to perform some action. That action is to pop the Modal on a screen. And to do so, we created a Modal and passed down few props. Code for the same is as follows:
import React from 'react'
function Modal({closeModal, name}) {
return (
<div className='modal'>
<div className='modalContainer'>
<p>{name}</p>
<div>
<button onClick={() => {closeModal(false)}}>×</button>
</div>
</div>
</div>
)
}
export default Modal
The expected result is for a Modal to pop with "You clicked button One", supposing we clicked one something similar to this.
The actual result is that all three Modals pop up one above the other when any of the three buttons are passed. The result:
I realize that I am passing the states wrong way. When any of the button is clicked all three get set to true. I simply don't realize how. Don't they create a method for each one?
Also, can you guys please teach me a better/understandable way to write clicked logic. Like maybe
if(clicked){
<Modal closeModal={setClicked} name={`You Clicked ${name}`} />
}
Because you bind all three buttons with one state, You need a state as array, with items equal to the number of buttons.
const [clicked, setClicked] = useState([false, false, false])
return (
<div className='CustomButton'>
<Navbar />
{
clicked.map((button, i) => {
return <Button setClicked={setClicked} name="Button Three" clicked={clicked[i]} index={i}/>
})
}
</div>
)
Then in the button component.
function Button({setClicked, name, clicked, index}) {
return (
<div>
<button onClick={() => {setClicked(prev => prev.map((item, i) => {
return i === index ? true : item
}))}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
And the modal component.
function Modal({ closeModal, name, index }) {
return (
<div className="modal">
<div className="modalContainer">
<p>{name}</p>
<div>
<button
onClick={() => {
closeModal((prev) =>
prev.map((item, i) => {
return i === index ? false : item;
})
);
}}
>
×
</button>
</div>
</div>
</div>
);
}
You can find a working example on this link.
https://codesandbox.io/s/old-wood-zgjno9
You can implement multiple modals like this:
import { useState } from "react";
export default function App() {
const [showModal1, setShowModal1] = useState(false);
const [showModal2, setShowModal2] = useState(false);
return (
<div className="App">
<button onClick={(e) => setShowModal1(true)}>Button 1</button>
<button onClick={(e) => setShowModal2(true)}>Button 2</button>
{showModal1 && (
<Modal text="Modal 1" onClose={(e) => setShowModal1(false)} />
)}
{showModal2 && (
<Modal text="Modal 2" onClose={(e) => setShowModal2(false)} />
)}
</div>
);
}
const Modal = ({ text, onClose }) => {
return (
<div>
{text}
<button onClick={onClose}>Close</button>
</div>
);
};
Working example
I have built a modal to display login/register modal. By default, the modal is opened by another component using the props show. This working when the modal is called by this component.
Also the modal Form is called from my Header.js as shown below:
<LoginRegisterForm displayPopUp={this.state.showLogin} onHide={() => this.setState({ showLogin: false })}/>}
In this case, the state showLogin is set to true when clicking on the Login/Register, the <LoginRegisterform is now showing the modal because displayPopup props is set to true
The code is below:
Form.js
const Form = ({ initialState = STATE_SIGN_UP, displayPopUp}) => {
const [mode, toggleMode] = useToggle(initialState);
const [display, toggleDisplay] = useToggleDisplay(displayPopUp);
console.log('----------------------------------------------------------')
console.log('displayPopUp: ' + displayPopUp)
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('----------------------------------------------------------')
return (
<Modal className="modal" show={displayPopUp} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={toggleDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={toggleDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>
);
};
in the FormLogin, I do have a Cancel button which allow me to close the modal located in the Form.js when needed. However, I do not know how I can make the modal close by change the show params in the Form.js when the close control is in the class FormLogin
FormLogin.js
import React from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import SocialButton from './styled/SocialButton'
import SlidingForm from './styled/SlidingForm'
import WhiteButton from '../../materialdesign/WhiteButton'
import { faFacebook, faGoogle, faLinkedinIn } from '#fortawesome/free-brands-svg-icons'
import Auth from '../../../data/network/Auth';
import Constant from '../../../config/Constant';
import CancelIcon from '#material-ui/icons/Cancel';
class FormLogin extends React.Component {
constructor(props, context) {
super(props);
this.state = {
email: '',
password: '',
loading: false,
error: '',
toggleDisplay: this.props.toggleDisplay
};
}
requestSignIn = async (event) => {
event.preventDefault();
this.setState({loading: true})
try {
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem(Constant.ALL, authData)
sessionStorage.setItem(Constant.AUTH_TOKEN, authData.token)
sessionStorage.setItem(Constant.DISPLAY_NAME, authData.user_display_name)
sessionStorage.setItem(Constant.EMAIL, authData.user_email)
sessionStorage.setItem(Constant.NICENAME, authData.user_nicename)
window.open("/", "_self") //to open new page
this.setState({loading: false })
this.close()
} catch (error) {
console.warn("Connection to WP - Auth Token failed ")
console.error(error);
}
}
requestForgotPassword = () => {
}
handleOnChange = (event) => {
this.setState({[event.target.name]: event.target.value})
}
render(){
const { email, password } = this.state;
return(
<SlidingForm>
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false) }/>
</div>
<h1 style={titleStyle}>Sign in</h1>
<div style={{textAlign: "center"}}>
<SocialButton>
<FontAwesomeIcon icon={faFacebook} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faGoogle} />
</SocialButton>
<SocialButton>
<FontAwesomeIcon icon={faLinkedinIn} />
</SocialButton>
</div>
<p style={txtStyle}>or use your account</p>
<form style={{textAlign: "center"}}>
<input style={formStyle} placeholder="Email" type="text" name="email" value={ email } onChange={ this.handleOnChange }/>
<input style={formStyle} placeholder="Password" type="password" name="password" value={ password } onChange={ this.handleOnChange } />
</form>
<p style={txtSpan}>
<a href="#" onClick={this.requestForgotPassword}>Forgot your password?</a>
</p>
<div style={{textAlign: "center", marginTop: "15px"}}>
<WhiteButton text="Sign in" onClick={this.requestSignIn}></WhiteButton>
</div>
</SlidingForm>
);
}
}
export default FormLogin
For now I was doing this :
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.state.toggleDisplay(false)
but it's not working, it's seems not having control on the Form.js.
toggleDisplay code is below:
import { useState } from 'react'
export const STATE_SHOW = true
export const STATE_HIDE = false
const useToggleDisplay = initialDisplayState => {
const [display, setDisplay] = useState(initialDisplayState)
const toggleDisplay = () =>
setDisplay(display === false ? true : false)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
console.log('display: ' + display)
console.log('toggleDisplay: ' + toggleDisplay)
console.log('-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%')
return [display, toggleDisplay]
}
export default useToggleDisplay
The Overall logic:
Modal is called from Header.js and show is set to false by default and switch to true when clicking on the menu option login
The Modal Form.js is handling login and register screen
What is the best option to be able to set show to false in the Form.js when the close is triggered in the FormLogin.js ?
Thanks
Instead of assigning the toggleDisplay prop to local state, just invoke the prop directly. This should work for updating the <Form />'s display state to false.
Also, do you intend for the <CancelIcon /> to toggle the modal open/close state, or is it just to close the modal? If it's the latter, you may want to update the prop name to closeModal instead of toggleDisplay.
<div style={{textAlign:"left"}}>
<CancelIcon style={{ color: "#ff7255" }} onClick={() => this.props.toggleDisplay(false)
}/>
</div>
Your useToggleDisplay func is confusing, and the original version was not accepting any arguments for toggleDisplay, hence even though you passed in false, it did not update the display state. I've removed useToggleDisplay since it doesn't do anything special.
const [display, setDisplay] = useState(initialDisplayState)
I also realised that <Modal /> is accepting displayPopUp instead of display. If you use displayPopUp, it doesn't know that display has been set to false, and therefore, remain open. And I passed setDisplay setter to the <FormLogin /> components.
<Modal className="modal" show={display} size="lg">
<Container pose={mode === STATE_LOG_IN ? "signup" : "login"}>
<div className="container__form container__form--one">
<FormLogin mode={mode} toggleDisplay={setDisplay} />
</div>
<div className="container__form container__form--two">
<FormSignup mode={mode} toggleDisplay={setDisplay}/>
</div>
<Overlay toggleMode={toggleMode} mode={mode} />
</Container>
</Modal>
I am fairly new to Reactjs and am running into an issue. I am using Griddle to display my data and using a Custom Component as a Column Definition. This custom component is defined outside of the class that contains the Griddle render and contains two buttons - Delete and Edit. I need to be able to click on either button and launch a modal, which I am using React Modal for. If I put a button within the class I can launch the modal as expected, however, when I am trying to launch the modal from one of the Edit or Delete buttons it does not work and I get an error
Uncaught TypeError: M.handleOpenModal is not a function.
Here is the code for the component that uses the Griddle plugin:
const actionComponent = ({ value }) =>
<div>
<i className="fas fa-edit"></i> Edit
<i className="fas fa-times"></i> Delete
</div>
const handleModalOpen = function () {
LinersTable.handleOpenModal();
}
export default class LinersTable extends React.Component {
constructor(props) {
super(props);
this.state = { liners: [], station: '', loading: true, selectedRowId: 0 };
this.modalElement = React.createRef();
this.options = {
defaultSortName: 'title', // default sort column name
defaultSortOrder: 'desc' // default sort order
};
}
handleOpenModal() {
alert();
}
render() {
let contents = this.state.loading ? <p><em>Loading...</em></p> : this.renderLinersTable(this.state.liners);
return <div>
<button className='btn btn-primary' onClick={() => { this.refreshData() }}>Refresh</button>
<a href="LinerReads/Studio" className='btn btn-primary'>Studio View</a>
<a href="LinerReads/Add" className='btn btn-primary'>Add</a>
<LinersStations onChange={this.handleStationChange.bind(this)} />
<ModalWindow ref={this.modalElement} />
{contents}
</div>;
}
renderLinersTable(liners) {
return (
<Griddle
styleConfig={styleConfig}
data={liners}
plugins={[plugins.LocalPlugin]}
components={{ Layout: NewLayout }}>
<RowDefinition>
<ColumnDefinition id="title" title="Title" order={1} width={600} />
<ColumnDefinition id="startDateString" title="Start" order={2} width={200} />
<ColumnDefinition id="endDateString" title="End" order={3} width={200} />
<ColumnDefinition id="linerID" title="Actions" customComponent={actionComponent} width={200} />
</RowDefinition>
</Griddle>
)
}
}
I have tried moving the Custom Component inside the class
handleModalOpen = function () {
this.handleOpenModal();
}
handleOpenModal() {
alert();
}
but it then throws an error
ReferenceError: handleModalOpen is not defined
Uncaught (in promise) ReferenceError: handleModalOpen is not defined
Anyone have a suggestion as to what I am missing or doing incorrect?
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)
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}/>