Hi I am trying to change the state of a component during the render. The state should change the classname depending on the list passed to it as props. I have tried but it does not seem to work. I can pass props but not change the state.
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {alive: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
alive: !state.alive
}));
};
render() {
return <div className = { this.state.alive ? "square--green" : "square--grey" } onClick = { this.handleClick } />;
};
}
function SquareList(props) {
const oxGrid = props.oxGrid;
const listItems = [];
oxGrid.forEach((item, i) => {
if(item === 'O'){
listItems.push(<Square key= {i}/>)
}
else {
listItems.push(<Square key = {i} />)
}
});
return listItems;
};
let printer = (function () {
let print = function (oXGrid) {
return ReactDOM.render(<SquareList oxGrid ={oXGrid} />, grid);
};
return { print: print };
})();
I have made the following changes in Square and SquareList Component. You need to pass a prop item to the Square Component.
class Square extends React.PureComponent {
constructor(props) {
super(props);
const isALive = this.props.item === 'O';
this.state = {
alive: isALive
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
alive: !state.alive
}));
};
componentWillReceiveProps(nextProps) {
if(this.props.item !== nextProps.item) {
this.setState({
alive: nextProps.item === '0'
});
}
}
render() {
return <div className = { this.state.alive ? "square--green" : "square--grey" } onClick = { this.handleClick } />;
};
}
function SquareList(props) {
const oxGrid = props.oxGrid;
const listItems = oxGrid.map((item, i) => {
return(
<Square item={item} key= {i}/>
)
});
return listItems;
};
let printer = (function () {
let print = function (oXGrid) {
return ReactDOM.render(<SquareList oxGrid ={oXGrid} />, grid);
};
return { print: print };
})();
Related
I need to call different function based on event and condition.
Let me explain on this example:
export class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: true,
};
this.login = () => {
this.setState({isLoggedIn: true})
};
this.logout = () => {
this.setState({isLoggedIn: false})
};
this.handleLogin = () => {
};
}
componentDidMount() {
const {store} = this.props;
store.on('isLoggedIn').subscribe(isLoggedIn => {
this.handleLogin = isLoggedIn ? this.logout : this.login;
});
}
render() {
const {isLoggedIn} = this.state;
return (
<NavLink to={{pathname: `${prefix}login`, isLoggedIn}}>
<div role="presentation" onClick={this.handleLogin}>isLoggedIn ? 'Logout' : 'Login'}</div>
</NavLink>
);
}
}
based on isLoggedIn eventlistener i need to set proper onClick reference. So i will change the state and sent via Navlink. Then i can listen to ComponentDidUpdate in redirected component.
redirected component lifecycle method:
ComponentDidUpdate({location}, prevState) {
{isLoggedIn: isLoggedInPrev} = location;
{isLoggedIn} = this.props;
if(isLoggedIn !== isLoggedIn) {
// do something
}
}
Do anybody knows the trick?
EDIT - i did some "workaround" :
...
this.handleLogin = doLogin => {
if (doLogin) {
this.login();
} else {
this.logout();
}
};
}
componentDidMount() {
const {store} = this.props;
if (store.get('isLoggedIn')) this.setUserFromJwt();
store.on('isLoggedIn').subscribe(isLoggedIn => {
this.setState({doLogin: !isLoggedIn});
});
}
...
in render
<NavLink to={{pathname: `${prefix}login`, isLoggedIn}} activeClassName={ACTIVE_CLASS}>
<div role="presentation" onClick={() => this.handleLogin(doLogin)}>{store.get('isLoggedIn') ? loggedInHeader : 'Login'}</div>
</NavLink>
now the state corresponds the action i need. But in Login componant no change in props. Both prevProps.location and this.props.location are unchanged.
Please HELP!
I fill data in componentdidupdate
componentDidUpdate(prevProps) {
if(isEmpty(this.props.tofiConstants)) return;
const { doUsers, dpUsers } = this.props.tofiConstants;
if (prevProps.cubeUsers !== this.props.cubeUsers) {
this.setState({
data: somefunc(doing here something)
});
}
console.log(this.state.data);
}
and then i want use the state in render
render() {
return (
<div className="profileScreen">{this.state.fullname}</div>
);
}
constructor is here
constructor(props) {
super (props);
this.state = {
data: []
};
}
function typeContactGetter is binded to this and everything is working, the only issue is in the functions return on the <li> element, I am trying to set a className coming from state and it returns undefined for this.state.
Why is this happening?
Thanks,
Bud
component
class ContactType extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"),
friendContacts: this.typeContactGetter("Friends")
};
this.typeContactGetter = this.typeContactGetter.bind(this);
this.handleClick = this.handleClick.bind(this);
this.hideList = this.hideList.bind(this);
}
handleClick = (event) => {
event.preventDefault();
console.log('clicked, state: ' + this.state.hiddenList);
};
hideList = () => {
console.log("this is hidelist: " + this.state.hiddenList);
if (this.state.hiddenList === true){
this.setState({
hiddenList: false
});
}
this.setState({
hiddenList: !this.state.hiddenList
});
};
typeContactGetter = (name) => {
console.log(this.state);
for (let contact of CONTACTS) {
if (contact.name === name) {
return (
<li className={this.state.hiddenList ? 'hidden' : ''} onClick={this.handleClick} key={contact.id.toString()}>
{contact.contacts.map(value => {
if (value.type === "Contact") {
return (
<a key={value.id.toString()} href="#">{value.name}</a>
);
}
})
}
</li>
);
}
}
};
render() {
return (
<ContactView familyContacts={this.state.familyContacts} friendContacts={this.state.friendContacts} hideList={this.hideList}/>
);
}
}
export default ContactType;
That's because you call typeContactGetter in the constructor before the state is actually created.
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"), // hey, but we are actually creating the state right now
friendContacts: this.typeContactGetter("Friends")
};
}
Why do you want to keep a component list in the state? Maybe it is better to pass them directly:
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
};
}
....
<ContactView familyContacts={this.typeContactGetter("Family")} friendContacts={this.typeContactGetter("Friends")} hideList={this.hideList}/>
btw you don't need to bind function as they are bound already by arrow functions.
import React, { Component } from 'react';
import DisplayTable from './Table.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuItems: this.props.menu_items,
searchString: '',
displayItems: this.props.menu_items
}
this.search = this.search.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.props.get_menu_items_api(false);
}
componentWillReceiveProps(nextProps) {
this.setState({ menuItems: nextProps.menu_items })
}
handleChange(e, isEnter) {
const searchData = () => {
let tempMenuProductDetails = this.props.menu_items;
const filterArray = tempMenuProductDetails.reduce((result, category) => {
if (category.categoryName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1) {
result.push(category);
}
if (category.productList && category.productList.length > 0) {
category.productList = category.productList.reduce((productListResult,
productList) => {
if (!!productList.productName &&
productList.productName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1)
{
productListResult.push(productList);
}
return productListResult;
}, []);
}
return result;
}, []);
this.setState({
displayItems: filterArray
}, function () {
console.log(this.state.displayItems);
})
console.log(filterArray);
}
if (!isEnter) {
this.setState({
searchString: e.target.value
});
} else {
searchData();
}
}
search(e) {
if (e.keyCode == 13) {
this.handleChange(e, true);
}
this.handleChange(e, false);
}
render() {
console.log(this.state.displayItems);
console.log(this.props.menu_items);
console.log(this.state.menuItems);
return (
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} /> )
}
}
export default App;
I have this search function in this file that does not update the value of props coming from the container of redux. Now when I pass {this.state.displayItems} in menu ,it does not display the data.
But when I pass {this.props.menu_items} it displays the data and I am not able to modify this.props.menu_items on the basis of search.
I have tried this code . what should i do?
The problem seems to be that, initially this.props.menu_items is an empty array and only after some API call the value is updated and you get the returned array on the second render, thus if you use it like
<DisplayTable dataProp={this.props.menu_items} editFuncProp=
{this.props.edit_menu_items_api} />
it works. Now that you use
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} />
and displayItems is only initialized in the constructor which is only executed once at the time, component is mounted and hence nothing is getting displayed.
The solution seems to be that you update the displayItems state in componentWillReceiveProps and call the search function again with the current search string so that you search results are getting updated.
Code:
import React, { Component } from 'react';
import DisplayTable from './Table.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuItems: this.props.menu_items,
searchString: '',
displayItems: this.props.menu_items
}
this.search = this.search.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentWillMount() {
this.props.get_menu_items_api(false);
}
componentWillReceiveProps(nextProps) {
this.setState({ menuItems: nextProps.menu_items, displayItems: nextProps.menu_items })
this.handleChange(null, true);
}
handleChange(e, isEnter) {
const searchData = () => {
let tempMenuProductDetails = this.props.menu_items;
const filterArray = tempMenuProductDetails.reduce((result, category) => {
if (category.categoryName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1) {
result.push(category);
}
if (category.productList && category.productList.length > 0) {
category.productList = category.productList.reduce((productListResult,
productList) => {
if (!!productList.productName &&
productList.productName.toLowerCase()
.indexOf(this.state.searchString.toLowerCase()) > -1)
{
productListResult.push(productList);
}
return productListResult;
}, []);
}
return result;
}, []);
this.setState({
displayItems: filterArray
}, function () {
console.log(this.state.displayItems);
})
console.log(filterArray);
}
if (!isEnter) {
this.setState({
searchString: e.target.value
});
} else {
searchData();
}
}
search(e) {
if (e.keyCode == 13) {
this.handleChange(e, true);
}
this.handleChange(e, false);
}
render() {
console.log(this.state.displayItems);
console.log(this.props.menu_items);
console.log(this.state.menuItems);
return (
<DisplayTable dataProp={this.state.displayItems} editFuncProp=
{this.props.edit_menu_items_api} /> )
}
}
export default App;
I'm trying to update the state of my component inside of an eventListener. I'm getting the following console error:
'Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Header component'
This is my component code:
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
fixed: false
}
}
handleScroll(event) {
this.setState({
fixed: true
});
}
componentDidMount() {
window.addEventListener("scroll",() => {
this.handleScroll();
});
}
componentWillUnmount() {
window.removeEventListener("scroll",() => {
this.handleScroll();
});
}
render() {
var {
dispatch,
className = "",
headerTitle = "Default Header Title",
onReturn,
onContinue
} = this.props;
var renderLeftItem = () => {
if (typeof onReturn === 'function') {
return (
<MenuBarItem icon="navigation-back" onClick={onReturn}/>
)
}
};
var renderRightItem = () => {
if (typeof onContinue === 'function') {
return (
<MenuBarItem icon="navigation-check" onClick= {onContinue}/>
)
}
};
return (
<div className={"header " + className + this.state.fixed}>
{renderLeftItem()}
<div className="header-title">{headerTitle}</div>
{renderRightItem()}
</div>
)
}
}
Header.propTypes = {
};
let mapStateToProps = (state, ownProps) => {
return {};
};
export default connect(mapStateToProps)(Header);
IMHO this is because you do ont unregister the function as you expect it, and a scroll event is sent after an instance of this component has been unmounted
try this:
componentDidMount() {
this._handleScroll = this.handleScroll.bind(this)
window.addEventListener("scroll", this._handleScroll);
}
componentWillUnmount() {
window.removeEventListener("scroll", this._handleScroll);
}