Setting state with onClick in render function - javascript

I have a button in render(), and I want it's onClick() to set the state. I know you shouldn't be setting the state in render() because it causes an infinite loop, so how should I go about this?
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
}
onCancel() {
this.setState({ edit: false, value: this.props.value });
}
onClick() {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.onClick}>
BUTTON
</div>
);
}
Updated to show what the code I'm trying now and the warning I'm getting thousands of times:
Warning: setState(...): Cannot update during an existing state transition (such as
within `render` or another component's constructor). Render methods should be a
pure function of props and state; constructor side-effects are an anti-pattern, but
can be moved to `componentWillMount`.
warning # warning.js?0260:44
getInternalInstanceReadyForUpdate # ReactUpdateQueue.js?fd2c:51
enqueueSetState # ReactUpdateQueue.js?fd2c:192
ReactComponent.setState # ReactComponent.js?702a:67
onCancel # mybutton.js?9f63:94
onClick # mybutton.js?9f63:98
render # mybutton.js?
...

Not really sure what you want to do since the previous answers didn't solve the issue. So if you provide some more information it might get easier.
But here is my take on it:
getInitialState() {
return (
edit: true
);
}
handleEdit() {
this.setState({edit: true});
}
handelCancel() {
this.setState({edit: false});
}
render() {
var button = <button onClick={this.handelEdit}>Edit</button>
if(this.state.edit === true) {
button = <button onClick={this.handelCancel}>Cancel</button>
}
return (
<div>
{button}
</div>
);
}

To set the state for your use case you need to set the state somewhere but I wouldn't do it this way. I would bind a function to the onClick event.
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
this.handleButtonClick = this.onClick.bind(this);
}
onCancel() {
this.setState({ edit: false, value: this.props.value });
}
onClick() {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.handleButtonClick}>
BUTTON
</div>
);
}
Look here for more information

Try to make use of arrow functions to bind onBtnClick and onCancel function to the context and see if it solves your problem.
function initialState(props) {
return {
edit: false,
value: props.value,
};
}
export default class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = initialState(props);
}
onCancel = ()=> {
this.setState({ edit: false, value: this.props.value });
}
onBtnClick = () => {
this.state.edit ? this.onCancel() : this.setState({ edit: true });
}
render() {
return (
<div onClick={this.onBtnClick}>
BUTTON
</div>
);
}

Related

HandleClick not changing the state

I have the below where it should display images of beers retrieved from an API. Each image has a handleClick event which will direct them to a details page about this beer. My code below doesn't render the beers at all and goes straight to the details page of a random beer. Can anyone help me figure out why?
Thanks
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
this.getBeerInfo = this.getBeerInfo.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick(details) {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo() {
...gets info
}
}
When you use onClick like that you run the function at the render.
So you have to use arrow function:
Not Working:
<img className = "img-beer" onClick = {this.handleClick(each)} src={each.image_url}/>
Working:
<img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
The main issue is not calling the handle properly.
Also, I noticed that you are binding the functions in the constructor. It might be simpler to use ES6 function creation, so the scope of the class is bound to your handle method.
export default class GetBeers extends React.Component {
constructor() {
super();
this.state = {
beers: [],
showMethod: false,
beerDetails: []
};
}
handleClick = (details) => {
this.setState({
showMethod: !this.state.showMethod,
beerDetails: details
});
}
render() {
if(this.state.showMethod) {
return (
<Beer details = {this.state.beerDetails}/>
);
}
else {
return (
<div>{this.state.beers.map(each=> {
return <img className = "img-beer" onClick = {() => this.handleClick(each)} src={each.image_url}/>
})}</div>
);
}
}
componentDidMount() {
this.getBeerInfo()
}
getBeerInfo = () => {
...gets info
}
}

React - How to set a state on a children when clicking on the parent

I have a search bar on a children component who's displaying a pannel when i'm writing inside,
and I'm trying to close it on my parent when I'm clicking outside of this pannel.
So I need a solution to set a state to false at this moment.
Any idea ?
Here's an example of my code:
[parent.js]
constructor(props) {
super(props)
this.state = {
showResults: false
}
}
render() {
return (
<div onClick={() => this.setState({showResults: false})}>
<div>
<Children showResults={this.state.showResults}/>
</div>
</div>)
}
[children.js]
constructor(props) {
super(props)
this.state = {
showResults: this.props.showResults
}
}
getResults(event) {
this.setState({input: event.target.value, showResults: true})
}
render() {
return (<div>
<Input onChange={this.getResults.bind(this)}/>
this.state.showResults ?
<div>PANNEL</div>
: ''
</div>)
}
When the panel is open, create a transparent div that covers the screen behind your panel with an onClick property to change the state.
I finally did it using Redux, I have created a ShowPannel reducer who just have a boolean visible state inside. Here's my code:
[My view] :
render() {
return (<div onClick={() => this.props.showPannel(false)}>
{
this.props.visible
? <div className="listResults"></div>
: ''
}
</div>)
}
function mapStateToProps(state) {
return {visible: state.showPannelReducer.visible};
}
function mapDispatchToProps(dispatch) {
return {
showPannel: (visible) => dispatch(showPannel(visible))
}
}
Home = connect(mapStateToProps, mapDispatchToProps)(Home);
export default Home;
[My reducer] :
import {SHOW_PANNEL} from '../actions/pannel/types';
const initialState = {
visible: false
};
function showPannelReducer(state = initialState, action) {
switch (action.type) {
case SHOW_PANNEL:
return {
...state,
visible: action.visible
}
default:
return state
}
};
export default showPannelReducer;

Strange class members

I get confused by the 'state' and 'login' members of a class definition from a running example as below:
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state
|| { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
For the 'login', I wan to get confirmation that this is a function member of the Login class, right? I can understand the motivation of using an arrow function is a matter of binding of 'this', but I did not see this syntax appeared in my ES6 book. It looks like in the top level of {}, it just defined a variable which is assigned with an arrow function.
For the 'state', this looks like a simple assignment, but I know it must be defining a member of the 'Login' since there is a 'this.state' reference. But I don't understand the syntax, my ES6 book says any instance property must be defined in constructor of the class. Is there any other special meaning of this kind of definition?
The standard way of defining initial state in React is like this:
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
redirectToReferrer: false
};
}
....
}
But, there are some libraries like unstated, that allow you to define state like this:
// BookContainer.js
import { Container } from 'unstated'
class BookContainer extends Container {
state = {
books: [],
booksVisible: false
}
addBook = book => {
const books = [...this.state.books, book]
this.setState({ books })
}
toggleVisibility = () => {
this.setState({
booksVisible: !this.state.booksVisible
})
}
}
export {
BookContainer
}
EDIT: Regarding to the login method, as you already told, is about binding the this
This:
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
Is the same as doing this:
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
redirectToReferrer: false
};
this.login = this.login.bind(this); // Bind the this
}
login(){
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true }); // This is not undefined
});
}
}
You can find more info in the official unstated page

How to write "Expire in" HOC?

I'm attempting to write a HOC that changes its state attribute visible to false after some time.
Here is what I've got so far:
const withExpire = (WrappedComponent) =>
class extends Component {
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
}
render() {
const expire_in = this.props.expire_in || 3000;
if (this.state.visible) {
setTimeout(() => {
this.setState({ visible: false });
}, 1000);
}
return <WrappedComponent visible={this.state.visible} {...this.props} />;
}
}
I've checked that code inside if (this.state.visible) runs, but it doesn't change my visible attribute.
Can someone explain me what I'm missing?
EDIT
SOLUTION:
const withExpire = (WrappedComponent) =>
class extends Component {
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
this.timeoutID = null;
}
componentWillMount () {
const expire_in = this.props.expire_in || 3000;
if (this.state.visible) {
this.timeoutID = setTimeout(() => {
this.setState({ visible: false });
}, expire_in);
}
}
componentWillUnmount () {
if (this.timeoutID) {
window.clearTimeout(this.timeoutID);
}
}
render () {
return <WrappedComponent {...this.props} visible={this.state.visible} />;
}
}
You actually implemented the perfect example against HOC usage. You can't be sure about what's coming in from the outside as props.
In this example the external visible property through {...this.props} overrides the visbile={this.state.visible} property.
A quick fix (by swapping the order of property definitions):
...
return <WrappedComponent {...this.props} visible={this.state.visible} />;
...
Also, don't forget to properly handle the Timer. You should store any timers and if still active cancel them in componentWillUnmount. Otherwise a still running timer in an unmounted component might cause errors thrown around.

React converting getInitialState (ES5) to this.state (ES6) with a property and method

I have issue with converting my react code to new format. Can you help me.
Now I have
function getStateFromFlux() {
return {
...
};
}
const TasklistsPage = React.createClass({
getInitialState() {
return {
...getStateFromFlux(),
isCreatingTaskList: false
};
},
....
_onChange() {
this.setState(getStateFromFlux());
}
});
I want smth like this one. But I have an error from my api request "error: TypeError: Cannot read property 'map' of undefined
at TasklistsPage.render"
function getStateFromFlux() {
return {
taskLists: TaskListsStore.getTaskLists()
};
}
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
func: getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
....
render() {
...
return (
...
{
this.state.taskLists.map(list =>
<ListItem
key={list.id}
leftIcon={<FolderIcon />}
primaryText={list.name}
onClick={router.history.push.bind(null, `/lists/${list.id}`)}
/>
)
}
...
)
}
_onChange() {
this.setState({func:getStateFromFlux()});
}
}
I don't know what I'm doing wrong.
Thanks for your help.
Instead of
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
func: getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
Try doing
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
...getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
In the first case, you simply assigned the return value of getStateFromFlux() to the key func. So your tasklist was under this.state.func.taskList. By doing a spread with the function, it will just place the contents of the object it created within the object you pass it into. You can alternatively just do the . operator on the return value from the getStateFromFlux() function.
In your onChange I would advise doing the same, you are assigning it to the variable named func in your code.
_onChange() {
this.setState({ ...getStateFromFlux() });
}

Categories

Resources