How to assign a prop value to a state in react - reactjs

I have an overlay which is launched from another React component which have a button that also changes itself. The change happens when the user clicks the button, then the button changes it's classname. It also sends a prop value to the children component which is the overlay. The overlay adds a class depending on the property and if it is clicked. Everthing is working pretty nice, but now I have to do another thing. When a user click on the overlay, it have to close up. Before this change, everything was working for the button I mentioned earlier.
Well this is the button component:
export default class StatusBarButton extends React.Component {
constructor(props) {
super(props)
this.state = {active: false}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ active: !this.state.active });
}
render() {
var cx = require('classnames');
var button = cx ({
'statusbar-button-container' : true,
'active' : this.state.active
});
var animation = cx ({
'statusbar-button-cross-image' : true,
'rotate' : true,
'active' : this.state.active
});
return (
<div className="astatusbar-center-wrapper">
<div className={button} onClick={this.handleClick}>
<img src="images/navbar-element-icon-cross.png" className={animation}/>
</div>
<OverlayView isOpen={this.state.active} />
</div>
);
}
}
And this is the Overlay at the moment:
export default class OverlayView extends React.Component {
constructor (props){
super(props);
this.state = {open: false}
this.setState({ open: this.props.isOpen });
}
render() {
var cx = require('classnames');
var overlay = cx({
'overlay': true,
'overlay-slidedown': true,
'open': this.state.open
});
return (
<div className={overlay} onClick={this._closeOverlay}>
<div className="overlay-menu-wrapper">
<OverlayNewInvoiceButton />
<OverlayNewBudgetButton />
<OverlayNewTicketButton />
</div>
</div>
);
}
}
As you see I'm trying to get the prop value and assign it to the state, but it's not working. How can I assign the prop value to the state and use it?
Regards,
JP

You are missing componentWillReceiveProps(props) method in your OverlayView component.
export default class OverlayView extends React.Component {
constructor (props){
super(props);
this.state = {open: props.isOpen}
}
componentWillReceiveProps(props) {
this.setState({open: props.isOpen});
}
render() {
let open = '';
if (this.state.open === true) {
open = 'open';
}
let overlayClass = `overlay overlay-slidedown ${open}`;
return (
<div className={overlayClass} onClick={this._closeOverlay}>
<div className="overlay-menu-wrapper">
<span>{open}</span>
</div>
</div>
);
}
}
Full working example:
https://codesandbox.io/s/35wLR4nA

constructor(props, context) {
super(props, context);
this.state = {
value: ''
};
}
componentWillReceiveProps(){ //this is called to before render method
this.setState({
value:this.props.data
})

You're problem might be that you are trying to fetch the class props with this. I think you are supposed to use the props passed into the constructor.
Like this:
constructor (props){
super(props);
this.state = {open: false}
this.setState({ open: props.isOpen });
}
Not sure why you aren't doing it in one line like this though: this.setState = { open: props.isOpen };

Finally i conserved the property which changes the class, is not bad that way. and i fixed with the help of this: Pass props to parent component in React.js and of course the help of a work mate. the end version is this:
This is the parent:
export default class StatusBarButtonView_Profit extends React.Component {
constructor(props) {
super(props)
this.state = {active: false}
this._openOverlay = this._openOverlay.bind(this)
this._closeOverlay = this._closeOverlay.bind(this)
}
_openOverlay() {
if (this.state.active == false) {
this.setState({ active: true });
console.log('boton lanza overlay');
} else {
this.setState({ active: false });
console.log('boton cierra overlay');
}
}
_closeOverlay(Overlay) {
if (this.state.active == true) {
this.setState({ active: false });
} else {
this.setState({ active: true });
}
}
render() {
var cx = require('classnames');
var button = cx ({
'aui-profit-statusbar-button-container' : true,
'active' : this.state.active
});
var animation = cx ({
'aui-profit-statusbar-button-cross-image' : true,
'rotate' : true,
'active' : this.state.active
});
return (
<div className="aui-profif-statusbar-center-wrapper">
<div className={button} onClick={this._openOverlay}>
<img src="images/aui-navbar-element-icon-cross.png" className={animation}/>
</div>
<OverlayView_Profit isOpen={this.state.active} onClick={this._closeOverlay}/>
</div>
);
}
}
this is the child:
export default class OverlayView_Profit extends React.Component {
constructor (props){
super(props);
}
_closeOverlay() {
this.props.onClick();
}
render() {
var cx = require('classnames');
var overlay = cx({
'aui-overlay': true,
'aui-overlay-slidedown': true,
'open': this.props.isOpen
});
return (
<div className={overlay} onClick={this._closeOverlay.bind(this)}>
<OverlayNewInvoiceButtonView_Profit />
<OverlayNewBudgetButtonView_Profit />
<OverlayNewTicketButtonView_Profit />
</div>
);
}
}
now everything works fine.

Related

REACTJS JSX re render

I want to re-render html in App.js what is triggered by click event.
In first load JSX component <WaypointList waypoint_is_done={false} App={this}/> is rendered.
But when i click button then it wont render JSX component <WaypointList waypoint_is_done={true} App={this}/> again.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
content: this.index()//LETS LOAD CONTENT
};
this.index = this.index.bind(this);
this.toggleDoneList = this.toggleDoneList.bind(this);
};
index() {
return <WaypointList waypoint_is_done={false} App={this}/>;
};
//SET NEW RENDERER ONCLICK
toggleDoneList(){
console.log('click');
this.setState({
content: <WaypointList waypoint_is_done={true} App={this}/>
//content: <div>see</div>
});
};
render() {
console.log(this.state.content);
return this.state.content;
};
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
First time it fire WaypointList class, but when i click button "object-done-listing" then not
It calls the App.toggleDoneList and App.render is also fired and it get correct JSX component but does not fire WaypointList class again
class WaypointList extends React.Component {
constructor(props) {
super(props);
this.App = props.App;
this.state = {
content: this.index(props)
};
this.index = this.index.bind(this);
};
index(props) {
let rows = logistic_route_sheet_waypoint_rows;
if (rows.length > 0) {
return (
<div className="map-listing">
<div className="object-done-listing noselect btn btn-success"
onClick={() => this.App.toggleDoneList()}>
<i className="fa fa-list" aria-hidden="true"></i>
</div>
</div>
);
}
return (null);
};
render() {
return this.state.content;
};
}
It works if i set
this.setState({
content: <div>see</div>
});
but not with
this.setState({
content: <WaypointList waypoint_is_done={true} App={this}/>
});
What is the problem ?
I found a solution to re-renderer the class
i made "CustomEvent" "reRenderer" and i call re_renderer function outside of react.

How to setProps in ReactJS

I have 2 Components one called NodeWidget and another called PopupWidget. In the NodeWidget it has a Model assigned to it which looks like the following:
PopupModel
export class PopupModel {
question: string;
model: string;
constructor(question: string, model: string) {
this.question = question;
this.model = model;
}
}
The parent Component is NodeWidget which passes in the Model to the PopupWidget with data in.
NodeWidget
{ this.state.showComponent ?
<PopupWidget model={this.props.popupModel} /> :
null
}
Then finally in the child Component we have this code:
export interface PopupWidgetProps {
model: PopupModel;
}
export interface PopupWidgetState { }
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props);
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.props.model.question} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
I want to be able to bind the value of the input to the model and then for it to update the original model in the Parent Component, am i doing this correctly as it does not seem to work.
You can do this to pass the input result to parent component on the button click:
PopupWidget :
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { question: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.inputResult(this.state.question)
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.state.question} onChange={(question) => { this.setState({ question })}} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
NodeWidget :
constructor(props) {
super(props);
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.props.inputResult(question);
this.setState({ showComponent: false });
}
...
{ this.state.showComponent ?
<PopupWidget inputResult={this.getInputResult} /> :
null
}
Finally in PopupModel (i assume this is a react component, i don't know if you can work with simple es6 class in react):
export class PopupModel extends React.Component {
constructor(props) {
this.state = { question: '', model: '' }; // set your initial state
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.setState({ question }); // here's our result from the input
}
render(){
return(<NodeWidget inputResult={this.getInputResult} />);
}
}
This can be pretty boring to handle if you have multiple components between the two which have to communicate.
You can use a HOC like Redux or MobX to handle an app state that can be passed in any component, and any component can dispatch actions to update the app state, you should go for it if you have multiple cases like this.

Passing value from Button to setState in array

I am trying to created boolean filters in an array.
If a button is "Active" (True) it should add the button name to the array ("selected").
If the button is "Inactive" (false), it should remove it from the array.
However, only some values end up in set state. I put it in a codepen:
https://codesandbox.io/s/wpxD35Oog
import React from 'react';
import { render } from 'react-dom';
import { x } from './data.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: x.movies,
};
}
render() {
const uniqueGenres = []
.concat(
...this.state.movies.map(movies =>
movies.genres.map(genres => genres.name),
),
)
.filter((genre, i, _) => _.indexOf(genre) === i);
return (
<div>
<div>
{uniqueGenres.map(e => <Filter1 key={e} result={e} />)}
<br />
</div>
</div>
);
}
}
class Filter1 extends React.Component {
constructor(props) {
super(props);
this.state = {
active: true,
selected: '',
};
}
handleActive = e => {
// this.setState(previousState => ({
// selected: [...previousState.selected, e.target.value]
// }));
console.log('pre-setState', e.target.value);
const active = !this.state.active;
const selected = e.target.value;
this.setState({
active: active,
selected: selected,
});
console.log('status', this.state.active, this.state.selected);
};
render() {
return (
<span>
<button onClick={this.handleActive} value={this.props.result}>
{this.props.result} {}<b>
{this.state.active ? 'Active' : 'Inactive'}
</b>
</button>
</span>
);
}
}
Ideally you would let the parent component (in this case, App) take care of managing the active genres. I rewrote part of your code to demonstrate it: https://codesandbox.io/s/JZWp1RQED

React not re-rendering on state change

I am rendering a new form when the button is clicked. So basically I change a state within the component from:
false to true or
null to true
However, it is strange that component does not re-render after the state change
export default class BoardManager extends React.Component{
constructor(){
super();
this.state = {
newForm : null
};
setTimeout(() => {
this.state = {
newForm : true
};
console.log(this.state.newForm);
},1000);
}
render(){
return(
<div>
Some thing here
{this.state.newForm ? <NewBoardForm />: null}
</div>
)
}
}
Help much appreciated!!!
Edit!!! Solution
export default class BoardManager extends React.Component{
constructor(){
super();
this.state = {
newForm : null
};
}
render(){
return(
<div>
<BoardsTable boards={BoardData.boards}/>
<button onClick={() => {
this.setState({
newForm : true
});
console.log(this.state.newForm);
}}>Add New</button>
<button onClick={() => {
this.setState({
newForm : null
});
console.log(this.state.newForm);
}}>Delete</button>
{this.state.newForm ? <NewBoardForm />: null}
</div>
)
}
}
Move the setTimeout call to inside a componentDidMount and use this.setState in there
You have to use this.setState({newForm: true}) instead of this.state = {newForm : true}
And put setState in other lifecycle stages.
You can use this.forceUpdate() at the end of onClick.
Reference:
https://facebook.github.io/react/docs/component-api.html

How to keep focus Style (added class) until you click out (onBlur) in a component

Im refactoring and improving a component into React ES6. Basically is a search box imput into a div with styles. the main fuction is when you click the div itself add a class to change style and idicate that you are focusing it. The classnames toggle works fine but i have to do something different, i have to keep the style of the focus until you click out even you click again on the imput. The problem is that if i do another click on the search box itself it toggles the class. How i can keep the focused class until user do onBlur?
import React from "react";
class StatusBarSearchBox extends React.Component {
constructor(props) {
super(props)
this.state = {focused: false}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ focused: !this.state.focused });
}
render() {
var cx = require('classnames');
var focusedConainer = cx ({
'statusbar-searchbox-container' : true,
'focus' : this.state.focused
});
var focusedIcon = cx ({
'statusbar-searchbox-icon' : true,
'focus' : this.state.focused
});
var actionButton = cx ({
'statusbar-searchbox-action-button' : true,
'focus' : this.state.focused
});
return (
<div className={focusedConainer} onClick={this.handleClick} onBlur={this.handleClick} >
<div className={focusedIcon}></div>
<input className="statusbar-searchbox-input" type="text" placeholder="BUSCAR..." />
<div className={actionButton}></div>
</div>
);
}
};
module.exports = StatusBarSearchBox;
onBlur works fine except when you do 2 clicks on the search box and then you click ouside because it toggles the style.
You could add the event handler conditionally.
<div
className={focusedConainer}
onClick={!this.state.focused && this.handleClick}
onBlur={this.state.focused && this.handleClick}
>
You should also probably rename the handleClick method to something like toggleFocus.
Finally i got this component, it works fine, but the next improvement is to do autofocus on the input even clicking outside but into the container div of the search box. Is that possible??
Here is the code:
import React from "react";
class StatusBarSearchBoxView extends React.Component {
constructor(props) {
super(props)
this.state = {focused: false}
this.toggleSearchBox = this.toggleSearchBox.bind(this)
this.clickOut = this.clickOut.bind(this)
}
toggleSearchBox() {
if (this.state = {focused: false}) {
this.setState({ focused: !this.state.focused });
} else {
this.setState({ focused: this.state.focused });
}
}
clickOut() {
this.setState({ focused: !this.state.focused });
}
render() {
var cx = require('classnames');
var focusedConainer = cx ({
'statusbar-searchbox-container' : true,
'focus' : this.state.focused
});
var focusedIcon = cx ({
'statusbar-searchbox-icon' : true,
'focus' : this.state.focused
});
var actionButton = cx ({
'statusbar-searchbox-action-button' : true,
'focus' : this.state.focused
});
return (
<div className={focusedConainer} onClick={this.toggleSearchBox} onBlur={this.clickOut} >
<div className={focusedIcon}></div>
<input className="statusbar-searchbox-input" type="text" placeholder="BUSCAR..." />
<div className={actionButton}></div>
</div>
);
}
};
module.exports = StatusBarSearchBoxView;

Resources