I'm having an issue with editing a value in my React app. I'm aware of how controlled components work, and my problem isn't related to that.
I can paste text into it and see state for the input change, but when I try to change it myself nothing happens. The input resides inside of a TableHeader component.
import React from "react";
import classNames from "classnames";
class TableHeader extends React.Component {
constructor(props) {
super(props);
this.state = {
columnFilterText : "",
filterBoxOpen : false
}
this.toggleSortBox = this.toggleSortBox.bind(this);
this.handleColumnInputChange = this.handleColumnInputChange.bind(this);
}
toggleSortBox(event, value) {
if(event.target === event.currentTarget) {
this.setState({
filterBoxOpen: !this.state.filterBoxOpen
});
}
}
handleColumnInputChange(event) {
console.log(event)
this.setState({
columnFilterText: event.target.value
})
}
render() {
let tableHeaderClasses = classNames({
"sortable" : true,
"filter-box-open" : this.state.filterBoxOpen
});
let sortOptionClasses = classNames({
"sort-option" : true
});
return (
<th className={tableHeaderClasses} onClick={this.toggleSortBox}>
<div className="sort-box">
<div className="sort-option-container">
<div className={sortOptionClasses}>Sort - ascending</div>
</div>
<div className="sort-option-container">
<div className={sortOptionClasses}>Sort - descending</div>
</div>
<hr className="divider" />
<input onChange={this.handleColumnInputChange} type="text" value={this.state.columnFilterText} />
<div className="row">
<button className="six columns">Apply</button>
<button className="six columns">Clear</button>
</div>
</div>
{this.props.label}
</th>
);
}
}
export default TableHeader;
I've checked if it's a css issue, by disabling styles, but the input field is still disabled, so no luck there. Any ideas what might be causing the issue?
I'm an idiot. The reason for this was that I had a keyDown event on the parent component for keyboard navigation, with event.preventDefault(); which of course affected the child component.
Thanks to everyone for contributing and for trying to help!
Related
I have a ticket as you can see in the picture below:
I have a delete button as a component and I am trying to add delete functionality to it. I am using this component in my ticket component. So this is my delete component:
<template>
<div id="delete-button" #click.prevent="removeProductFromCart(item.id)">
<input type="checkbox" id="checkbox">
<div id="bin-icon">
<div id="lid"></div>
<div id="box">
<div id="box-inner">
<div id="bin-lines"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
data() {
return {
loading: false,
};
},
methods: {
removeProductFromCart(id) {
this.loading = true;
setTimeout(() => {
cartHelper.removeFromCart(id, (response) => {
this.$store.dispatch('removeProductFromCart', {
cart: response.data,
})
this.loading = false
});
}, 1000)
}
}
};
</script>
So the parent component is ticket component:
[![<template>
<div id="sold-tickets">
<div class="card">
<div class="sold-tickets-actions properties">
<div class="sold-tickets-inner">
<DeleteButton :item = "item" />
</div>
</div>
</div>
</div>
</template>][1]][1]
<script>
import image from "../../../../img/Hallenbad.jpg";
import DeleteButton from "./DeleteButton";
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
components: {DeleteButton},
data() {
return {
image: image,
};
},
};
</script>
My problem is, the ticket is being deleted even when I click outside of the child component (Delete component). But I want to use the delete component like a button and I only want to delete the ticket when it is clicked, not outside of the button.
Try to stop propagation. You probably do not even need the prevent modifier.
#click.prevent.stop='...'
or
#click.stop='...'
If the display type is block, you might also want to inspect the div to see if it is actually filling the entire width of the page. If so, use an inline type, a flex layout, or similiar.
Thanks for taking the time to read this. I have been at this now for a few hours and nearly got it right except now, Im Getting a "TypeError: render is not a function" Error and Can not figure it out, using Context API and Reactjs Please help
I have changed around States and props and back again this is the closest I got it to work
this is my parent file where I pass the UserProvider
import React, { Component } from "react";
import Group from "./Groups";
import JsonData from "../data/export";
import { UserProvider } from "./context/UserContext";
class GroupGrid extends Component {
render() {
// console.log(JsonData);
return (
<UserProvider value={JsonData}>
<div className='cards'>
<Group />
</div>
</UserProvider>
);
}
}
export default GroupGrid;
this is the child file
import React, { Component } from "react";
import UserContext from "../components/context/UserContext";
import { UserConsumer } from "../components/context/UserContext";
class GroupDetails extends Component {
static contextType = UserContext;
componentDidMount() {
const data = this.context;
// console.log(data); // { name: 'Tania', loggedIn: true }
}
render() {
return (
<UserConsumer>
{this.context.TransportGrid.GROUPS.map((value, index) => (
<div key={value.GROUP_ID} className='GroupDetail'>
<div className='groupRow1'>
<span className='Date_label'>Date:</span>
<span className='GroupDate'>
{value.GROUP_DATE}
</span>
<span className='Group_label'>Group No:</span>
<span className='GroupID'>{value.GROUP_ID}</span>
</div>
<div className='groupRow2'>
<span className='Driver_label'>Driver:</span>
<span className='DriverName'>
{value.DRIVER_NAME}
</span>
<span className='Reg_label'>Artic:</span>
<span className='VehcileReg'>
{value.VEHICLE_REG}
</span>
</div>
</div>
))}
</UserConsumer>
);
}
}
export default GroupDetails;
and this an example of the JSON it looking at
{
"TransportGrid": {
"GROUPS": [
{
"GROUP_ID": "1234",
"GROUP_DATE": "20/08/2019",
"DRIVER_ID": "22",
"DRIVER_NAME": "JIMMY BLOGGS",
"VEHICLE_REG": "XSRFDFDDF",
"START_COUNTRY": "COUNTRY1",
"END_COUNTRY": "COUNRTY2",
"MOVEMENTS": [
{
this is repeated like 180 times ANyone got any idea's?
Thanks in Advance
Problem is that your consumer from context API needs to just have a single children element as a function whereas you are having an array of JSX elements as children.
Also since you are using the latest context API you don't need to use the Consumer component, you could simply write your code as
class GroupDetails extends Component {
static contextType = UserContext;
componentDidMount() {
const data = this.context;
// console.log(data); // { name: 'Tania', loggedIn: true }
}
render() {
return (
<React.Fragment>
{this.context.TransportGrid.GROUPS.map((value, index) => (
<div key={value.GROUP_ID} className='GroupDetail'>
<div className='groupRow1'>
<span className='Date_label'>Date:</span>
<span className='GroupDate'>
{value.GROUP_DATE}
</span>
<span className='Group_label'>Group No:</span>
<span className='GroupID'>{value.GROUP_ID}</span>
</div>
<div className='groupRow2'>
<span className='Driver_label'>Driver:</span>
<span className='DriverName'>
{value.DRIVER_NAME}
</span>
<span className='Reg_label'>Artic:</span>
<span className='VehcileReg'>
{value.VEHICLE_REG}
</span>
</div>
</div>
))}
</React.Fragment>
);
}
}
export default GroupDetails;
I have a div with a className of results and I want to make so that when I click on the Check button the div gets populated with the info from my store: Shop1, Shop2. I added an observable field on my CardCheck component that you toggle with the onClick event handler, and when the field is true it should display all entries in the auto array.
Here is my Component:
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import PropTypes from 'prop-types';
import './cardCheck.css';
#inject('auto')
#observer
class CardCheck extends Component {
onClick = event => {
event.preventDefault();
this.checked = !this.checked;
};
render() {
const { auto } = this.props;
return (
<div>
<div className="newsletter-container">
<h1>Enter the ID of your card:</h1>
<div className="center">
<input type="number" />
<input type="submit" value="Check" onClick={this.onClick} />
</div>
<div className="results">{this.checked && auto.auto.map(a => <div key={a.name} />)}</div>
</div>
<h1>Offers:</h1>
</div>
);
}
}
CardCheck.propTypes = {
auto: PropTypes.shape({
carCount: PropTypes.string
})
};
export default CardCheck;
and here is my store:
import { observable, action, computed } from 'mobx';
class Auto {
#observable
auto = [
{
name: 'Shop1'
},
{
name: 'Shop2'
}
];
#observable checked = false;
#action showInfo
}
}
export { Auto };
Right now nothing happens when I click on the Check button, why, and how can I make it finally work?
I tried iterating through the object with a loadash method but it did not populated the div, and I also tried this in the div results:
{this.checked &&
auto.auto.map((a, index) => <div key={index}>{a.name}</div>)}
but it didn't work and it gave me this error: Do not use Array index in keys react/no-array-index-key
I think you have mistyped with checked field. It should be this.auto.checked
I would like to have modals and notifications in my app and coming from using old jQuery Bootstrap, creating modals and notifications were really easy but now I am pretty confused on how to implement this in the virtual DOM using the react component system.
This is what I believe the standard react way to build modals in React within a component:
Index/Router Component >
Main Layout Component >
{...Page Components... }
{...Child Component}
{<Modal /> or <Notification />}
The issue with this is I dont want to constantly have to import and create a <Modal> or <Notification /> component within my sub components, instead maybe just call a utility function such as {app.notify({type: 'success', message: 'some message'})} or app.modal({...customconfig}) and have both defined within my Main layout component which get triggerd through any child components.
Any help on this would be great, thanks!
You do not need to keep your Modal component in a hierarchy. Your Modal component should be an independent component which would take appropriate props to decide what needs to be displayed. E.g.
<Modal message={"This is my modal"} showOkCancel={true} showYesNo={false} handleOkYes={()=>console.log("OK clicked")} handleCancelNo={()=>console.log("Cancel clicked"} />
In the above example, the Modal accepts a number of props which would help it decide the message to display, the buttons to display and the actions that need to take on said button click.
This kind of a component can reside outside your component hierarchy and can be imported into any component that needs to show a modal. The parent component would just need to pass the appropriate props to show the modal.
Hope this helps.
So here is the approach I took to resolve this.
First here is how you want to structure the modal and notification components:
{Index/Router Component}
{Main Layout Component <Modal /> or <Notification />}
{...Page Components... }
{...Child Component calls app.modal({...config}) or app.notify(...config)}
For notifications, I used a plugin called react-notification-system and for modal, I just wrote it myself.
Here is my code:
Layout.js
import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';
import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";
import Modal from "#/ui/modals/modal/Modal";
import "#/main.scss";
import './layout.scss';
export default class Layout extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
app.notify.clear = this.refs.notificationSystem.clearNotifications;
app.notify = this.refs.notificationSystem.addNotification;
app.modal = this.refs.modal.updateProps;
}
render() {
return (
<div class="app">
<div class="header">
<AppHeader page={this.props.location.pathname.replace('/', '')}/>
</div>
<div class="body">
{this.props.children}
</div>
<div class="footer">
<AppFooter />
</div>
<NotificationSystem ref="notificationSystem" style={false} />
<Modal ref="modal" />
</div>
);
};
}
Modal.js
import React from "react";
import ReactDOM from 'react-dom';
import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
showHeader: true,
showFooter: false,
title: "",
size: '',
className: '',
id: '',
footerContent: null,
showSubmitBtn: true,
showCancelBtn: true,
cancelBtnText: "Cancel",
successBtnText: "Save Changes",
onModalClose: () => {},
showModal: false,
html: () => {}
}
this.updateProps = this.updateProps.bind(this);
this.hideModal = this.hideModal.bind(this);
}
componentWillMount() {
var self = this;
var $modal = $(ReactDOM.findDOMNode(this));
}
componentDidUpdate(prevProps, prevState) {
if(this.state.showModal) {
$('body').addClass('modal-open');
} else {
$('body').removeClass('modal-open');
}
}
componentWillUnmount() {
// $('body').removeClass("modal-open");
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
updateProps(args) {
let merged = {...this.state, ...args};
this.setState(merged);
}
hideModal() {
this.setState({
showModal: false
});
this.state.onModalClose();
}
buildFooter() {
if(this.props.footerContent) {
return (
<div class="content">
{this.props.footerContent}
</div>
)
} else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
return (
<div class="buttons">
<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
<button type="button" class="btn btn-success">{this.props.successBtnText}</button>
</div>
);
} else if(this.props.showCancelBtn) {
return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
} else if(this.props.showSubmitBtn) {
return (<button type="button" class="btn btn-success">Save changes</button>);
}
}
render() {
let {
id,
className,
onModalClose,
size,
showHeader,
title,
children,
showFooter,
showModal,
html
} = this.state;
return (
<div class={`modal-wrapper`} >
{
showModal ?
<div class={`modal fade in ${className}`} role="dialog">
<div class="bg" ></div>
<div class={`modal-dialog ${size}`}>
<div class="modal-content">
{ showHeader ?
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<SVGInline svg={closeSvg} />
</button>
<h4 class="modal-title">{ title }</h4>
</div> : '' }
<div class="modal-body" >
{html()}
</div>
{ showFooter ?
<div class="modal-footer">
{ this.buildFooter() }
</div> : ''
}
</div>
</div>
</div>
: ''
}
</div>
);
}
}
then in any of your child components you can just call within your render function:
app.notify({
message: message,
level: 'error'
});
or
app.modal({
showModal: true,
className: "fullscreen-image-modal",
size: "modal-lg",
html: () => {
return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
}
})
I've been struggling with debugging react. In jsbin, there's no way I can know what error it is, when I open the console or console of my broswer there's no clear indication of what my error is about.
http://jsbin.com/doletanole/1/edit?html,js,console,output
class HelloWorldComponent extends React.Component {
constructor() {
super()
this.getInput = this.focusHandler.bind(this)
this.state = {hasError:false}
}
focusHandler(e) {
if(e.target.value === ''){
this.setState({hasError:true})
}
}
render() {
return (
<input placeholder="username" type="text" onBlur={this.focusHandler}/>
{this.state.hasError ? <span>Username is required</span> : ''}
);
}
}
Any better way to debug react? I just want to show the error msg if when the user go away of the input base on the state.
Whenever binding the methods in the constructor, try to use the same name to avoid these kind of mistakes, i think you need to reset the state value to false if the username is not blank, Try this Code:
class HelloWorldComponent extends React.Component {
constructor() {
super()
this.focusHandler = this.focusHandler.bind(this)
this.state = {hasError:false}
}
focusHandler(e) {
this.setState({hasError: e.target.value != '' ? false : true});
}
render() {
return (
<div>
<input placeholder="username" type="text" onBlur={this.focusHandler}/>
{this.state.hasError ? <span>Username is required</span> : ''}
</div>
);
}
}
Check working example: http://jsbin.com/cozenariqo/1/edit?html,js,console,output
First of all, you must return only one top level element from a component (or an array, but that's less common). Wrap your rendered output in a single element:
render() {
return (
<div>
<input placeholder="username" type="text" onBlur={this.focusHandler}/>
{this.state.hasError ? <span>Username is required</span> : ''}
</div>
);
}
Secondly, you're not correctly binding the focusHandler event. Change it to onBlur={this.focusHandler.bind(this)}. Suggested reading: React, ES6, Autobinding, and createClass()
The error blocking your code from loading was from the wrapping element. JS Bin does not propagate Babel errors to the user well. I would suggest not using it and set up a local development environment with Babel and Webpack instead.