How do I call an event for another component in React? - reactjs

I've been working in react for about.. 2 days. 3/4's of that was me trying to get react to work with my current Asp.net MVC WebApp.
I have 2 classes. Everything is working fine, but I want the handleScan of Camera to call the handleHideClick() of ShowScanner. Whats the proper way to do that?
Here is my code:
import React from 'react';
import QrReader from 'react-qr-reader';
import { unmountComponentAtNode, render } from "react-dom";
class Camera extends React.Component {
state = {
result: 'No result'
}
handleScan = data => {
if (data) {
this.setState({
result: data
});
scanDataProcessor(this.state.result);
unmountComponentAtNode(document.getElementById('root'));
// *** Replace unmount above with a call to execute: ShowScanner.handleHideClick()
}
}
handleError = err => {
console.error(err)
}
render() {
return (
<div id="scanner" class="">
<QrReader
delay={300}
onError={this.handleError}
onScan={this.handleScan}
style={{ width: '100%' }}
/>
</div>
)
}
}
class ShowScanner extends React.Component {
constructor(props) {
super(props);
this.handleShowClick = this.handleShowClick.bind(this);
this.handleHideClick = this.handleHideClick.bind(this);
this.state = { showCamera: false }
}
handleShowClick() {
this.setState({ showCamera: true });
render(<Camera />, document.getElementById('root'));
}
handleHideClick() {
this.setState({ showCamera: false });
unmountComponentAtNode(document.getElementById('root'));
}
render() {
const showCamera = this.state.showCamera;
let button;
if (showCamera) {
return (
<button id="btnScan" type="button" class="btn btn-primary p-1" onClick={this.handleHideClick}>
<i class="bi bi-qr-code-scan"></i>
<br />Scan
</button>
);
} else {
return (
<button id="btnScan" type="button" class="btn btn-primary p-1" onClick={this.handleShowClick}>
<i class="bi bi-qr-code-scan"></i>
<br />Scan
</button>
);
}
}
}
render(<ShowScanner />, document.getElementById('scanBtn'));
At the very least, I need to reset the button so that when they click it, it remounts the Camera component without them having to click it twice.
Thanks in advance!

Related

Array has duplicated records when using checkboxes to populate an array using React

I have trouble with simple task of adding elements selected in checkboxes to an array in component state. It seems like the push method for state.toppings (Editor.js) is invoked twice for each checkbox click, even though console.log shows that updateFormValueCheck method is invoked once per click. Can anyone help?
This is App.js
import React, { Component } from "react";
import { Editor } from "./Editor";
import { Display } from "./Display";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
formData: {}
}
}
submitData = (newData) => {
console.log("newData", newData)
this.setState({ formData: newData });
}
render() {
return <div className="container-fluid">
<div className="row p-2">
<div className="col-6">
<Editor submit={this.submitData} />
</div>
<div className="col-6">
<Display data={this.state.formData} />
</div>
</div>
</div>
}
}
This is Editor.js
import React, { Component } from "react";
export class Editor extends Component {
constructor(props) {
super(props);
this.state = {
toppings: ["Strawberries"]
}
this.toppings = ["Sprinkles", "Fudge Sauce",
"Strawberries", "Maple Syrup"]
}
updateFormValueCheck = (event) => {
event.persist();
this.setState(state => {
if (event.target.checked) {
state.toppings.push(event.target.name);
} else {
let index = state.toppings.indexOf(event.target.name);
state.toppings.splice(index, 1);
}
}, () => this.props.submit(this.state));
}
render() {
return <div className="h5 bg-info text-white p-2">
<div className="form-group">
<label>Ice Cream Toppings</label>
{this.toppings.map(top =>
<div className="form-check" key={top}>
<input className="form-check-input"
type="checkbox" name={top}
value={this.state[top]}
checked={this.state.toppings.indexOf(top) > -1}
onChange={this.updateFormValueCheck} />
<label className="form-check-label">{top}</label>
</div>
)}
</div>
</div>
}
}
This is Display.js
import React, { Component } from "react";
export class Display extends Component {
formatValue = (data) => Array.isArray(data)
? data.join(", ") : data.toString();
render() {
let keys = Object.keys(this.props.data);
if (keys.length === 0) {
return <div className="h5 bg-secondary p-2 text-white">
No Data
</div>
} else {
return <div className="container-fluid bg-secondary p-2">
{keys.map(key =>
<div key={key} className="row h5 text-white">
<div className="col">{key}:</div>
<div className="col">
{this.formatValue(this.props.data[key])}
</div>
</div>
)}
</div>
}
}
}
The output is:
You cannot directly mutate this.state, it can only be done using this.setState. For more info. refer this: Why can't I directly modify a component's state, really?
Therefore, you need to update your Editor component as follows.
componentDidMount is used to display the initial state during the initial rendering. Then componentDidUpdate is used to render the state changes through display component whenever it's updated.
import React, { Component } from "react";
export class Editor extends Component {
constructor(props) {
super(props);
this.state = {
toppings: ["Strawberries"],
};
this.toppings = ["Sprinkles", "Fudge Sauce", "Strawberries", "Maple Syrup"];
}
updateFormValueCheck = (event) => {
event.persist();
let data;
if (event.target.checked) {
data = [...this.state.toppings, event.target.name];
} else {
const index = this.state.toppings.indexOf(event.target.name);
const temp = [...this.state.toppings];
temp.splice(index, 1);
data = temp;
}
this.setState({
toppings: data,
});
};
componentDidMount() {
this.props.submit(this.state.toppings);
}
componentDidUpdate(prevPros, prevState) {
if (prevState.toppings !== this.state.toppings) {
this.props.submit(this.state.toppings);
}
}
render() {
console.log(this.state);
return (
<div className="h5 bg-info text-white p-2">
<div className="form-group">
<label>Ice Cream Toppings</label>
{this.toppings.map((top) => (
<div className="form-check" key={top}>
<input
className="form-check-input"
type="checkbox"
name={top}
value={this.state[top]}
checked={this.state.toppings.indexOf(top) > -1}
onChange={this.updateFormValueCheck}
/>
<label className="form-check-label">{top}</label>
</div>
))}
</div>
</div>
);
}
}
Hope this would be helpful to solve your issue.

Open bootstrap modal after axios http call in react

I made an HTTP call in react using Axios. It works perfectly fine. But when I try to open a bootstrap 4 modal after HTTP call success. It shows me an error 'modal is not a function'. I try a number of ways to solve this but unable to solve the problem. I didn't upload the whole code as it is quite long. Let me know in the comments if you want any additional code sample. Please help.
import $ from 'jquery';
import '../assets/css/signup.css';
import { Link } from 'react-router-dom';
import axios from 'axios';
import SuccessMessage from './dashboard/SuccessMessage';
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
$('#signup-success').modal('show');
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
)
}
Success Message component
<div className="modal" id={this.props.modalId}>
<div className="modal-dialog modal-dialog-centered">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Modal Heading</h4>
<button type="button" className="close" data-dismiss="modal">×</button>
</div>
<div className="modal-body align-center" style={style}>
<i style={icon} className={this.props.iconClass} ></i>
<h3 className="heading color-black">{this.props.heading}</h3>
<p className="paragraph color-black">{this.props.description}</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Try not to use jquery and react together. You could achieve what you are saying using the react state:
class SignUp extends React.Component{
constructor()
{
super()
this.state={
firstName:'',
lastName:'',
email:'',
phoneNumber:'',
password:'',
confirmPassword:'',
isSignUp:false,
showModal: false
}
}
componentDidUpdate()
{
if(this.state.isSignUp === true)
{
let user = {
firstName: this.state.firstName,
lastName: this.state.lastName,
email:this.state.email,
phoneNumber:this.state.phoneNumber,
password:this.state.password
}
console.log(user);
var first_name = user.firstName;
var last_name=user.lastName;
var email=user.email;
var phone_no=user.phoneNumber;
var password = user.password;
axios.post("http://ec2-14-2a9-69-0b6.us-west-2.compute.amazonaws.com:4000/dashboard/register", {
first_name,
last_name,
email,
phone_no,
password
}, {
headers: header
})
.then(res => {
console.log(res);
if(res.status === 200 && res.data.success === true)
{
setTimeout(() =>
{
this.setState({ showModal: true });
},200)
}
})
}
}
handleSubmit=(e) =>
{
e.preventDefault();
this.setState({isSignUp:true});
}
render()
{
return(
<div>
{
this.state.showModal &&
<SuccessMessage heading="Sign Up Successfully!" description="Please login in to access your account" iconClass="fa fa-check bg-golden flex all-center border-radius-50" modalId="signup-success"/>
</div>
)
}
Also, I guess you got a display: none or something in the modal as you are doing a .show using jquery. Put that to display always as it will be only shown if the state is true.
Actually getting the Bootstrap Modal to display using React (without jQuery) requires DOM manipulation. Bootstrap 4 uses jQuery to add a modal backdrop element, adds the modal-open class to the body, and finally adds display:block to the .modal wrapper.
This is why it's preferable to using reactstrap, react-bootstrap, etc... since they've already componentized the Bootstrap Modal.
If you must show (toggle) the Bootstrap Modal in React without jQuery (or other component framework), here's an example:
class SuccessMessage extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
modalClasses: ['modal','fade']
}
}
toggle() {
document.body.className += ' modal-open'
let modalClasses = this.state.modalClasses
if (modalClasses.indexOf('show') > -1) {
modalClasses.pop()
//hide backdrop
let backdrop = document.querySelector('.modal-backdrop')
document.body.removeChild(backdrop)
}
else {
modalClasses.push('show')
//show backdrop
let backdrop = document.createElement('div')
backdrop.classList = "modal-backdrop fade show"
document.body.appendChild(backdrop)
}
this.setState({
modalClasses
})
}
render() {
return (
<div
id="messageModal"
className={this.state.modalClasses.join(' ')}
tabIndex="-1"
role="dialog"
aria-hidden="true"
ref="messageModal"
>
<div className="modal-dialog modal-dialog-centered modal-lg">
<div className="modal-content">
<div className="modal-header">
<h4>
Success
</h4>
...
</div>
<div className="modal-body">
...
</div>
</div>
</div>
</div>
)
}
}
Working Demo: https://codeply.com/p/4EV36QjwCB

ReactDOM.findDOMNode() return null with refs

ReactDOM is not working with refs.
I want to sting html <b>abcd</b> append to body of modal, but it's not working.
when I check console.log() of modal and modalBody, it return null.
My code in under:
class Header extends Component {
constructor(props) {
super(props);
this.state = {
this.state.modalSendResult: false
};
}
_open() {
this.setState({
modal: !this.state.modal
});
if (!this.state.modal) {
this.appendNode();
}
}
appendNode() {
let modal = ReactDOM.findDOMNode(this.refs.modal)
modalBody = ReactDOM.findDOMNode(this.refs.body);
ReactDOM.render(
<b>abcde</b>, modalBody
);
}
render() {
return (
<Button onClick={this._open.bind(this)}
className="btn btn-primary btn-primary-1 mr-1">
<i className="fa fa-paper-plane-o" aria-hidden="true"></i> Open
</Button>
<Modal ref="modal" isOpen={this.state.modal} toggle={this._open}
className={'modal-primary modal_customer'}>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody className="modal-send" ref="body">
</ModalBody>
</Modal>
);
}
}
Please help me!
Take a look at ReactDOM. You can avoid having to call findDOMNode by setting up your refs as specified here: Refs and the DOM.
I think in your case it would look something like
<Modal
ref={(modal) => { this.modal = modal; }}
isOpen={this.state.modal}
toggle={this._open}
className={'modal-primary modal_customer'}
>
<ModalHeader toggle={this._open}>
Danh sách kết quả gửi
</ModalHeader>
<ModalBody
className="modal-send"
ref={(body) => { this.body = body; }}
/>
</Modal>
Then you should be able to reference them with
this.modal
this.body
Alternatively, you could pass the content you want for your ModalBody component as a prop.
<ModalBody
className="modal-send"
content="abcde"
ref={(body) => { this.body = body; }}
/>
And render in your ModalBody component:
render() {
return (
<b>{this.props.content}</b>
);

React issue with composition - _constructComponentWithoutOwner [duplicate]

Everything seems to work with this small app except adding a new note. Button is located on the Board component.
i know this problem is usually caused by not binding value of 'this' properly. I'm not sure if that's the issue here or if i'm missing something else. Thanks
Demo: http://jsbin.com/pewahi/edit?js,output
/* jshint asi:true */
class Note extends React.Component {
constructor(props) {
super(props)
this.state = { editing: props.editing }
}
render() {
if (this.state.editing) {
return this.renderForm()
} else {
return this.renderDisplay()
}
}
edit() {
this.setState({editing: true})
}
save() {
this.props.changeHandler(this.refs.newText.getDOMNode().value, this.props.index)
this.setState({editing: false})
}
remove() {
this.props.removeHandler(this.props.index)
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button className="btn btn-sm glyphicon glyphicon-pencil" onClick={this.edit.bind(this)}></button>
<button className="btn btn-sm glyphicon glyphicon-trash" onClick={this.remove.bind(this)}></button>
</span>
</div>
)
}
renderForm() {
return (
<div className="note">
<textarea ref="newText" defaultValue={this.props.children} className="form-control"></textarea>
<button onClick={this.save.bind(this)} className="btn btn-success btn-sm"><span className="glyphicon glyphicon-floppy-disk"></span> Save</button>
</div>
)
}
}
Note.propTypes = {
editing: React.PropTypes.bool,
onChange: React.PropTypes.func,
onRemove: React.PropTypes.func
}
Note.defaultProps = { editing: false }
class Board extends React.Component {
constructor(props) {
super(props)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
update(newText, i) {
var arr = this.state.notes
arr[i].note = newText
this.setState({notes: arr})
}
remove(i) {
var arr = this.state.notes
arr.splice(i, 1)
this.setState({notes: arr})
}
addNote(text) {
var arr = this.state.notes
arr.push({
id: this.nextId(),
note: text
})
console.log(arr)
this.setState({notes: arr})
}
nextId() {
this.uniqueId = this.uniqueId || 0
return ++this.uniqueId
}
eachNote(note, i) {
return (
<Note key={note.id}
index={i}
changeHandler={this.update.bind(this)}
removeHandler={this.remove.bind(this)}
>{note.note}
</Note>
)
}
render() {
return (
<div className="board">
{this.state.notes.map(this.eachNote, this)}
<button onClick={this.addNote.bind(this, "new note")} className="btn btn-success btn-sm glyphicon glyphicon-plus"></button>
</div>
)
}
}
React.render(
<Board />,
document.getElementById('message-board')
)
Your code is fine. This is likely a bug with JSBin, and how it handles transpilation with Babel. If you add the pragma // noprotect to the top of your code you will see that it works.
I was facing the same error. I was using a base component and I noticed that I had removed componentDidMount method of the base component. And when I call super.componentDidMount in sub component it was giving the error. So I have removed super call and problem solved.
Binding this is something of a hassle with ES6 classes in React. One way is to bind them in your constructor like so;
constructor(props) {
super(props)
this.nextid = this.nextid.bind(this)
this.state = {
notes: [{note: 'hi', id: this.nextId()}]
}
}
Another is to use babel.configure({stage: 0}) and arrow functions.
nextid = () => {}

onClick in reactjs not working

Below is my code. My onClick is nor working. It always through error "Uncaught TypeError: Cannot read property 'likeQuestion' of undefined". But my "gotoPage" function is working. I don't know where I am wrong. I am very new in Reactjs. Why "likeQuestion" function is not recognized.
My first onClick is working
export default class Question extends React.Component {
constructor(){
super();
this.toggle = this.toggle.bind(this);
this.state = {
pageNo : 1,
dropdownOpen: false,
questioninfo : []
}
}
componentWillMount(){
//some action
}
gotoPage(index) {
//some action. This is working
}
toggle() {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
likeQuestion(e){
console.log('this is clicked');
//But this is not working
}
render() {
var canvases = this.state.questionItem.map(function(data,i) {
var firstLtr = data.user_name.charAt(0);
return (
<div key={i}>
<Col sm="12" md={{ size: 12, offset: 2 }} className="questionCard">
<Card block>
<CardTitle>
<div className="outerCircle"><span>{firstLtr}</span></div> {data.user_name}
<i className="fa fa-flag-o flagging" aria-hidden="true"></i>
{data.location_url}
</CardTitle>
<CardText className="questionTxt">{data.message}</CardText>
<div>
<Button className="replyBtn" disabled>No Discussion</Button>
<Button size="sm" color="link" className="disussionSpan" onClick={(i) => this.likeQuestion(i)}>{data.likes} Likes</Button>
</div>
</Card>
</Col>
</div>
);
});
return(
<div className="container">
<div className="row">
<div className="pageInfo">
<Dropdown className="inline" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
<DropdownToggle caret>
Pages
</DropdownToggle>
<DropdownMenu>
{pgrow}
</DropdownMenu>
</Dropdown>
<p className="inline currPgNo">Page: {currentPage}</p>
</div>
<div className="col-md-8 col-md-offset-2">
{canvases}
</div>
</div>
</div>
)
}
React wouldn't auto-bind map inside render(), so you have to do it yourself in order to use this and call this.likeQuestion. Luckily, map provides a second argument to specify the context (this).
So just use...
this.state.questionItem.map(function(data,i) {
...
}, this)
instead of
this.state.questionItem.map(function(data,i) {
...
})
Option 2: Use arrow function in the map, such as map((data, i) => ...
Option 3: bind this to likeQuestion in the constructor of the component.
Try to define your helper functions using arrow functions
gotoPage = (index) => {
//some action. This is working
}
toggle = () => {
this.setState({
dropdownOpen: !this.state.dropdownOpen
});
}
likeQuestion = (e) => {
console.log('this is clicked');
//But this is not working
}
or
Bind these methods in constructor of your React component. e.g
this.likeQuestion = this.likeQuestion.bind(this);
// Needs to be done for all the helper methods.
So that you access the class level this context.
E.g a minimal setup
class Question extends React.Component {
constructor(props) {
super(props);
this.state = {
likes:10
};
}
likeQuestion = (e) => {
console.log('this is clicked');
//But this is not working
}
render() {
return ( < div >
< button size = "sm"
color = "link"
className = "disussionSpan"
onClick = {
(i) => this.likeQuestion(i)
} > {
this.state.likes
}
Likes < /button>
< /div >
);
}
};
ReactDOM.render( < Question / > , document.querySelector('#test'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="test">
</div>

Resources