Please suggest how to Enable/Disable Toggle button from another component in ReactJS?
The idea is to Enable/Disable Toggle button from Statistics' fetchStatistics() function. When the logic of fetchStatistics() checks the response it should block Toggle button click or Enable click event for it.
ProviderContainer - root container
var ProviderContainer = React.createClass({
getInitialState: function() {
},
render() {
return (
<div className="app">
<Provider />
<Provider />
</div>
);
}
});
Provider component
var Provider = React.createClass({
getInitialState: function() {
},
render: function() {
return (
<li>
<Statistics />
<Toggle />
</li>
);
}
});
Statistics component
var Statistics = React.createClass({
fetchStatistics: function() {
let url = "https://localhost/statistics";
fetch(url)
.then(data => {
if (data) {
// Disable click for <Toggle> button
} else {
// Enable click for <Toggle> button
}
})
.catch((error) => {
console.log('error', error);
});
},
render: function(){
return(
<div>
...
</div>
);
}
});
Toggle component
class Toggle extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
}
componentWillReceiveProps(nextProps) {
}
render() {
return (
<div className="toogle active" onClick={this.handleClick} >
<div className="toggle__manage"></div>
</div>
);
}
};
See the code below. I have modified the Provider, Statistics, and Toggle components.
In the Provider component you'll need to manage a allowToggle state which is passed down to the Toggle component. The state can be changed via the setAllowToggle function which is passed down to the Statistics component.
var Provider = React.createClass({
getInitialState: function() {
return {allowToggle: false};
},
setAllowToggle: function(allowToggle) {
this.setState({ allowToggle });
},
render: function() {
return (
<li>
<Statistics setAllowToggle={this.setAllowToggle}/>
<Toggle allowToggle={allowToggle}/>
</li>
);
}
});
In the Statistics component the setAllowToggle and is called with true or false as per the condition.
var Statistics = React.createClass({
fetchStatistics: function() {
let url = "https://localhost/statistics";
fetch(url)
.then(data => {
if (data) {
// Disable click for <Toggle> button
this.props.allowToggle(false);
} else {
// Enable click for <Toggle> button
this.props.allowToggle(true);
}
})
.catch((error) => {
console.log('error', error);
});
},
render: function(){
return(
<div>
...
</div>
);
}
});
In the Toggle component the allowToggle prop is used to decide whether to allow toggle or not. Also, the active css class and the onClick function are conditionally provided.
class Toggle extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
}
componentWillReceiveProps(nextProps) {
}
render() {
return (
<div
className={`toggle ${this.props.allowToggle ? 'active' : ''`}
{...(this.props.allowToggle && {
onClick: this.handleClick
})}
>
<div className="toggle__manage"></div>
</div>
);
}
};
Related
in project i have 3 page
1)i need when i click in button 'Next' in page 1 redirect to page 2 and show toast Component in page2
2)when click'Next' in page 2 redirect to page 3
3)and when click button 'back' in page 3 redirect to page 2 but not show toast in page2,
How do slove this chalange, redirect work correctly i have problem in show toast or not show in page 2
---page1---
class page1 extends Component {
handleSubmit = (e) => {
this.props.history.push('/page2');
};
render() {
return (
<Fragment>
<Button
title={"Next"}
onClick={this.handleSubmit}
/>
</Fragment>
);
}
}
---page2 - i write state for toast in componentDidMount f---
class page2 extends Component {
constructor(props) {
super(props);
this.state = {
showToast: false,
messageToast: '',
levelToast: 'success',
}
}
componentDidMount() {
this.setState({
showToast: true,
messageToast: 'Ok',
levelToast: 'success',
}, () => {
setTimeout(() => this.setState({showToast: false}), 3000)
})
}
handleSubmit = () => {
this.props.history.push('/page3');
};
render() {
return (
<Fragment>
<Button
title={"Next"}
onClick={this.handleSubmit}
/>
</Fragment>
);
}
}
--page3---
class page3 extends Component {
handleBack = (e) => {
e.preventDefault();
this.props.history.push('/page2');
};
render() {
return (
<Fragment>
<Button
type={"close"}
title={"back"}
id={'b'}
onClick={this.handleBack}
/>
</Fragment>
);
}
}
Assuming you're using React Router, the easiest way would be to use the second argument that goes into the the history.push function. You could do something like:
Page1.js
class Page1 extends Component {
handleSubmit = (e) => {
this.props.history.push('/page2', {showToast: true});
// the second argument sets a state value that can be accessed in the `Page2` component
};
render() {
return (
<Fragment>
<Button
title={"Next"}
onClick={this.handleSubmit}
/>
</Fragment>
);
}
}
Page2.js
class Page2 extends Component {
constructor(props) {
super(props);
this.state = {
showToast: false,
messageToast: '',
levelToast: 'success',
}
}
componentDidMount() {
// this is the `state` argument in the `handleSubmit` function on Page1
const shouldShowToast = this.props.location.state && this.props.location.state.showToast
this.setState({
showToast: shouldShow,
messageToast: 'Ok',
levelToast: 'success',
}, () => {
setTimeout(() => this.setState({showToast: false}), 3000)
})
}
...
...
}
'Page3.js`
class Page3 extends Component {
handleBack = (e) => {
e.preventDefault();
this.props.history.push('/page2', {showToast: false}); // set the location state.showToast to `false` this time.
};
...
...
}
Hope that helped!
I have an entity with a date that within a datepicker component I need to change for a new date. To do that I'm ussing a popup. The popup work flawlessly if I click the datepicker and handle the change with the function this.props.handleDateChange but if the users types in the date with the keyboard I can't catch the new date and modify my parent state with the value in my datepicker this.props.myEntityCurrentDate. How can I achieve this?
I have the following HOC that corresponds to an popup in react. It receives a body and a footer.
const createPopup = (Body, Footer) => {
class Popup extends React.PureComponent {
render() {
return <div className='popup-react'>
<div className='popup-react-content'>
<div>
<div>
<span>{this.props.title}</span>
</div>
<div>
<Body {...this.props} />
</div>
<div>
<Footer {...this.props} />
</div>
</div>
</div>
</div>;
}
}
return Popup;
}
And I create the following body and footer for the desired page
class PopupBody extends React.PureComponent {
render() {
return <div>
<DatePicker
dateFormat="MM/DD/YYYY"
value={this.props.myEntityCurrentDate}
onChange={this.props.handleDateChange}
/>
</div>;
}
}
class PopupFooter extends React.PureComponent {
render() {
return <div>
<button type="button">
<span onClick={() => this.props.handleSubmit(this.props.myEntityId)}>Accept</span>
</button>
<button type="button" onClick={this.props.closePopup}>
<span className='ui-button-text'>Cancel</span>
</button>
</div>;
}
}
const Popup = createPopup(PopupBody, PopupFooter);
The component that will make use of those components and render the popup is the following:
class MyEntity extends React.Component {
constructor(props) {
super(props);
this.state = {
showPopup: true,
newDate: null,
errors: []
};
//bind of this
};
}
handlePopupDateChange(dueDate) {
this.setState({
newDate: dueDate,
errors: []
});
}
handlePopupSubmit(id) {
var validations = {
newDate: {
presence: {
allowEmpty: false, message: " can't be empty"
}
}
};
validate(this.state, validations, () => this.submitPopupForm(id), this.renderPopupErrors);
}
submitPopupForm(id) {
var vm = { Id: parseInt(id), DueDate: this.state.newDate ? this.state.newDate.format("DD-MM-YYYY") : null }
this.props.apiClient.post('/api/MyEntity/Put/',
vm,
this.formPopupSucceeded,
this.formPopupFailed);
}
formPopupSucceeded() {
this.togglePopup();
window.location.reload();
}
formPopupFailed(response) {
this.togglePopup();
this.renderPopupErrors([response.Message]);
}
renderPopupErrors(validationErrors) {
this.togglePopup();
this.setState({ errors: validationErrors });
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup,
newDate: null,
});
}
render() {
return <div>
<If condition={this.state.showPopup}>
<Popup
title='My Popup Title'
closePopup={this.togglePopup}
myEntityCurrentDate={myMappedEntity.Date}
handleDateChange={this.handlePopupDateChange}
handleSubmit={this.handlePopupSubmit}
Date={this.state.newDate}
myEntityId={myMappedEntity.Id} />
</If>
</div>;
}
}
Currently on my react app, I am loading many div's which is being dynamically loaded with info from a database. I am trying to make it so when I click on one of these div's a Pop-up emerges, with more in depth data related to that div. However, it does not seem to work as expected. The onClick does not work with this dynamically loaded div. I tried testing the pop-up on a standard button element on my main App component and it worked. Here is my code:
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
render() { ********** Below here is where my issue is *****
return (
this.state.posts.map(events =>
<div key={events.key}
className='result_box'
onClick={this.togglePopup.bind(this)}>
<p>{events.name}</p>
{events.place && events.place.location && <p>
{events.place.location.city}</p>}
</div>
)
{this.state.showPopup ?
<Result
text='Close Me'
closePopup={this.togglePopup.bind(this)}
/>
: null
}
);
}
}
And this ResultBox is being rendered in App
class App extends Component {
render() {
return (
<div className="App">
<NavBar className="navbar-body"/>
<div className="spacer"></div>
<p className="App-intro">
Find a jam
</p>
<ResultBox />
</div>
);
}
}
The Result is simply the pop-up box component. If anyone knows how I can get this to work it would be much appreciated.
Yes, you need to have your posts data in a div, this is how I would structure it.
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
this.togglePopup = this.togglePopup.bind(this);
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
buildRows() {
return this.state.posts.map( (events, index) =>
<div key={index} className='result_box' onClick={this.togglePopup}>
<p>{events.name}</p>
{events.place && events.place.location &&
<p>{events.place.location.city}</p>
}
</div>
);
}
render() {
let rows = this.buildRows();
return (
<div>
{rows}
{this.state.showPopup &&
<Result text='Close Me'closePopup={this.togglePopup.bind(this)}/>
}
</div>
);
}
}
export default ResultBox;
I have the following:
import React from 'react';
import {render} from 'react-dom';
class TShirt extends React.Component {
render () {
return <div className="thsirt">{this.props.name}</div>;
}
}
class FirstName extends React.Component {
propTypes: {
name: React.PropTypes.string.isRequired
}
constructor(props) {
super(props);
this.state = {
submitted: false
};
}
getName () {
var name = this.refs.firstName.value;
this.setState(function() {
this.props.action(name);
});
}
handleSubmit (e) {
e.preventDefault();
this.setState({ submitted: true }, function() {
this.props.actionNav('color');
});
}
render () {
if(!this.state.submitted){
return (
<div>
<h2>tell us your first name</h2>
<form>
<input
type="text"
ref="firstName"
onChange={this.getName.bind(this)}
/>
<div className="buttons-wrapper">
back
<button onClick={this.handleSubmit.bind(this)}>continue</button>
</div>
</form>
</div>
);
}
else {
return <PickColor color={this.props.colorVal} />;
}
}
}
class Link extends React.Component {
setActiveClass () {
if(this.props.el == this.props.activeClass){
return 'active';
}
}
render () {
var active = this.setActiveClass();
return (
<li className={active}>{this.props.el}</li>
);
}
}
class Nav extends React.Component {
render () {
var links = ['name', 'color', 'design', 'share'],
newLinks = [],
that = this;
links.forEach(function(el){
newLinks.push(<Link activeClass={that.props.active} key={el} el={el} />);
});
return (
<ul>
{newLinks}
</ul>
);
}
}
class PickColor extends React.Component {
getColorValue(event) {
console.log(event.target.getAttribute("data-color"));
console.log(this);
//this.props.color(event.target.getAttribute("data-color"));
}
render () {
var colors = ['red', 'purple', 'yellow', 'green', 'blue'],
colorsLink = [],
that = this;
colors.forEach(function(el){
colorsLink.push(<li data-color={el} key={el} onClick={that.getColorValue} ref={el}>{el}</li>);
});
return (
<ul>
{colorsLink}
</ul>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
color: '',
active: ''
};
this.getName = this.getName.bind(this);
this.setActiveNav = this.setActiveNav.bind(this);
this.setColor = this.setColor.bind(this);
}
getName (tshirt) {
this.setState({ name:tshirt })
}
setActiveNav (active) {
this.setState({ active:active })
}
setColor (color) {
this.setState({ color:color })
}
render () {
return (
<section className={this.state.color}>
<Nav active={this.state.active} />
<TShirt name={this.state.name} />
<FirstName name={this.state.name} action={this.getName} actionNav={this.setActiveNav} colorVal={this.setColor} />
</section>
);
}
}
render(<App/>, document.getElementById('app'));
Inside the "PickColor" component I am trying to do this:
getColorValue(event) {
console.log(event.target.getAttribute("data-color"));
console.log(this);
//this.props.colorVal(event.target.getAttribute("data-color"));
}
however this is returning null on click so I can't go ahead ans use:
this.props.colorVal
The solution was here:
class PickColor extends React.Component {
getColorValue(event) {
console.log(event.target.getAttribute("data-color"));
this.props.color(event.target.getAttribute("data-color"));
}
render () {
var colors = ['red', 'purple', 'yellow', 'green', 'blue'],
colorsLink = [],
that = this;
colors.forEach(function(el){
colorsLink.push(<li data-color={el} key={el} onClick={that.getColorValue.bind(that)} ref={el}>{el}</li>);
});
return (
<ul>
{colorsLink}
</ul>
);
}
}
I had to bind "that" to the onClick inside the forEach loop
Issue is in this line:
onClick={that.getColorValue}
You forgot to bind the correct this (class context) with onClick event handler function because of that this.props is not accessible in getColorValue function.
Solutions:
Multiple Solutions are possible, use any one (all these changes are for PickColor component):
1- Inline binding of click handler:
onClick = { that.getColorValue.bind(this) }
2- Bind the method in the constructor:
constructor(props) {
super(props);
this.state = { };
this.getColorValue = this.getColorValue.bind(this);
}
3- Using arrow function:
onClick = {e => that.getColorValue(e) }
4- Using class property syntax:
onclick = {this.getColorValue}
getColorValue = (e) => {
console.log('e', this.props)
}
When you create a new function, like this:
function() {
this.props.action(name);
});
This is bind to the new function context. Every function has a different this in javascript. You can solve this in a few ways:
Use arrow functions if you have them (they won't rebind this)
() => {
this.props.action(name);
});
Rebind this with bind
function() {
this.props.action(name);
}.bind(this));
Save this in a variable
var that = this;
function() {
that.props.action(name);
});
Choose the first if you have a transpiler, like babel! Otherwise it's your call.
I would like to add Event onMouseEnter to component prop without wrapper HTML tag.
var tooltipText = this.props.children;
<span onMouseEnter={this.renderTooltip}>{tooltipText}</span>
Is it possible add this event to this.props.children, because I can't afford on the wrapper span.
Here is all component code:
class FastTooltip extends Component{
constructor(props){
super(props);
this.renderTooltip = this.renderTooltip.bind(this);
this.state = {
tooltip: false,
};
}
renderTooltip(){
this.setState({ tooltip: true });
}
render(){
var renderTooltip;
var { overlay, placement } = this.props;
var tooltipText = this.props.children;
if( this.state.tooltip ){
renderTooltip = <OverlayTrigger placement={placement} overlay={<Tooltip>{overlay}</Tooltip>}>
{tooltipText}
</OverlayTrigger>
} else {
renderTooltip = <span onMouseEnter={this.renderTooltip}>{tooltipText}</span>
}
return renderTooltip;
}
}
If I understand correctly, you could map your component's children and return a new element with an event handler. Does something like this get you on the right track?
class FastTooltip extends Component {
render() {
return React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
onMouseEnter: () => { console.log('onMouseEnter'); }
})
});
}
}