React setState not updating state and UI - javascript

Here I try to clear ingredient when users click purchasing cancel, and set state purchasing false then ingredient clean, but state seems to be true. and doesn't clear ingredient from orders with realtime, even modal window cl what should i need to do ?
///Root Component
state = {
purchasing: false
}
purchaseCancleHandler = () => {
this.setState({purchasing: false
});
}
<OrderSummary
purchaseContinue = {
this.purchaseContinuewHandler
}
purchaseCancle = {
this.purchaseCancleHandler
}
/>
//Child component
import React, {Component} from 'react'
import Button from '../../UI/Button/Button'
import Aux from '../../../hoc/Aux'
class OrderSummary extends Component {
componentWillUpdate() {
//console.log('[OrderSummer] willupdate')
}
render ()
{
const ingredientSummary =Object.keys(this.props.ingredients)
.map(igkey => {
return <li key={igkey}><span style={{textTransform:'capitalize'}}>{igkey}</span>: {this.props.ingredients[igkey]}</li>
});
return(
<Aux>
<h3>Your Order</h3>
<p> A delicious Burger with the following ingredient</p>
<ul>
{ingredientSummary}
</ul>
<p>Total Price :<strong>{this.props.totalprice.toFixed(2)}</strong></p>
<p>Continure To Checkout ?</p>
<Button btnType="Danger" clicked={this.props.purchaseCancle}>CANCEL</Button>
<Button btnType="Success" clicked={this.props.purchaseContinue}>CONTINUE</Button>
</Aux>
);
}
}
export default OrderSummary;

state = {
purchasing: false
}
purchaseCancleHandler = () => {
this.setState({purchasing: false
});
}
<Button btnType="Danger" clicked={this.purchaseCancleHandler}>CANCEL</Button>
You don't need .props if it's declare in the same local state. Also, your function wasn't invoked spelled correctly (it was missing "Handler")

You're wanting to call a method defined on the component itself, not a prop passed to the component, so you need to use this.purchaseCancleHandler.
state = {
purchasing: false
}
purchaseCancleHandler = () => {
this.setState({
purchasing: false
});
}
<Button btnType="Danger" clicked={this.purchaseCancleHandler}>CANCEL</Button>

constructor(props) {
super(props)
this.state = {
purchasing: null
}
}
purchaseCancleHandler = () => {
this.setState({
purchasing: false // need set this value difference with initial value
});
}
// Child component
clicked = () => {
this.props.purchaseCancle()
}
<Button btnType="Danger" clicked={this.clicked}>CANCEL</Button>
To make sure component will render after setState, need set value for purchasing difference with initial value.

Related

Storing variable value in function component

So, in a React class component, I can add a property super easily:
export default class Thing extends Component {
constructor(props){
super(props)
this.hasRunQuery = false
}
handleClick = () => {
this.hasRunQuery = true
}
render(){
return (
<button onClick={this.handleClick}>
Click
</button>
)
}
When it unmounts/remounts, this variable is of course re-set (as it's a new instance of the class). Function components don't appear to have this luxury:
let hasRunQuery = false
export default () => {
handleSubmit = () => {
hasRunQuery = true
}
return (
<button onClick={handleSubmit}>
Click
</button>
)
}
If the above component unmounts and re-mounts, hasRunQuery will still be true. If there are 10 instances of this component, they'll somehow all share the same variable.
Short of storing any and all properties in useState, is there any means of setting variables in function components that don't keep their value once unmounted?
In functional components this is achieved with useRef
export default () => {
const hasRunQuery = useRef(false)
const handleSubmit = () => {
hasRunQuery.current = true
}
return (
<button onClick={handleSubmit}>
Click
</button>
)
}
For more information, see the api documentation, plus this section of the faq

React button radio this.setState is not a function

I have 2 component a parent component to manage the state and a lot of other things and a child component with some reactstrap buttons radio i'm trying to change the state onClick on the child buttons but I get the error: this.setState is not a function and i can't figure out what's wrong with my code =>
//Parent
import React, { Component } from 'react';
import BtnRadio from './btnToggle';
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
};
export default AddAdmin;
//Chlid
import React from 'react';
import { Button, ButtonGroup } from 'reactstrap';
const BtnRadio = (props) => {
return (
<ButtonGroup>
<Button color="light" onClick={() => props.onRadioBtnClick(true)} active={props.active === true}>Enable</Button>
<Button color="light" onClick={() => props.onRadioBtnClick(false)} active={props.active === false}>Disabled</Button>
</ButtonGroup>
);
};
export default BtnRadio;
is there someone who can point me to the right direction i guess that i forgot to bind something...
The problem is, when you're using non-anonymous functions, this gets overridden, and wont refer to the component anymore. Since you're already using class properties, the simple fix, is to keep using the arrow functions, to keep this referencing the component:
onRadioBtnClick = (rSelected) => {
this.setState({
rSelected:rSelected
});
}
See #5 in this medium article, which explains different ways of binding this to keep it referencing the component.
<BtnToggle onRadioBtnClick={() => this.onRadioBtnClick()} active={this.state.rSelected}/>
Arrow function for the rescue.
You should bind the functions your passing like so:
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick.bind(this)} active={this.state.rSelected}/>
</div>
);
}
}
alternatively, you can bind the functions before passing them in the constructor:
class parent extends Component {
state = {
rSelected: true,
}
constructor() {
super()
this.onRadioBtnClick = this.onRadioBtnClick.bind(this)
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
}

Changing the state of a checkbox with setState

I have a component and i am able to change the state of checkbox item when i click on a button.
I also want to be able to change the state in another button-click, which has lot more logic in it.
The issue i am having in that when the code goes into the condition it does not set the state back to false.
I tried changing the state again using setstate but it does not change the state of the enabledCheckBox.
this.setState({
enabledCheckBox: !this.state.enabledCheckBox,
})
Can anyone tell me what the issue is?
Thanks
class Customer extends Component {
constructor(props) {
super(props)
this.state = {
...props,
enabledCheckBox: false
};
}
//this works
onChangeCheckBox=()=>{
this.setState({
enabledCheckBox: !this.state.enabledCheckBox,
})
}
//cant get this to work
handleCustomerClick = (event) => {
if (this.state.enabledCheckBox) {
this.setState({
enabledCheckBox: !this.state.enabledCheckBox,
})
}
https://codesandbox.io/s/1y0zywy727
I included a working example. It does similar things as how you described.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
enabledCheckBox: false
};
}
onClick = () => {
this.setState({ enabledCheckBox: !this.state.enabledCheckBox });
};
render() {
return (
<div>
<input
type="checkbox"
value={"some checked box"}
checked={this.state.enabledCheckBox}
onChange={this.onClick}
/>
{this.state.enabledCheckBox ? <div>Blue</div> : <div>Red</div>}
<button onClick={this.onClick}>Click to Change Color</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Both input and div are using this.state.enabledcheckbox. So if you check the box, not only the box will be checked but also the div's "color" would change with it. Same thing as clicking the button
your this is being bound incorrectly try this;
handleCustomerClick = (event) => {
let {enabledCheckBox} = this.state;
this.setState({
enabledCheckBox: !enabledCheckBox,
});
}
If you really need the if statement you could do the following;
handleCustomerClick = (event) => {
let {enabledCheckBox} = this.state;
let data;
if (enabledCheckBox) {
data = {
enabledCheckBox: !enabledCheckBox
}
}
this.setState(data);
}

React update children from higher level parent

as you can see LoginContainer.js contains a component Form and inside form there are multiple children.
in Form.js i loop over the children add some custom method to the children props and finally output them.
the isFetching comes from the redux store.
in LoginContainer when isFetching is changed to true the FormButton component doesn't recieve the new prop value because its owned by the Form component.
i know why this is happening because the Form component is not changed directly and wont update so the children wont be rerendered.
is there a way that Form.js will update its children?
LoginContainer.js
#connect((store) => ({
isFetching: store.users.isFetching,
error: store.users.error
}), (dispatch) => ({
action: bindActionCreators(actionCreators, dispatch)
}))
class LoginContainer extends Component {
handleAuth(user) {
this.props.action.fetchAndHandleUser(user.email, user.password)
}
render() {
const { isFetching } = this.props
return (
<h1>Login to VMS</h1>
<Form onFormSubmit={this.handleAuth} formClass={styles.loginForm}>
....
<FormButton
type="submit"
buttonText="Login"
showLoader={isFetching} // default value is false
loaderText="Authenticating" />
</Form>
)
}
}
Form.js
class Form extends Component {
....
componentWillMount() {
this.children = {}
this.inputs = {}
this.model = {}
this.registerInputs(this.props.children)
}
registerInputs(children) {
this.children = React.Children.map(children, (child) => {
if(child.props.name) {
return React.cloneElement(child, {
bindToForm: this.bindToForm,
unbindFromForm: this.unbindFromForm,
validate: this.validate
})
}
if(child.props.children) {
this.registerInputs(child.props.children)
}
else {
return child
}
})
}
render() {
return (
<form onSubmit={this.handleSubmit} className={this.props.formClass}>
{this.children}
</form>
)
}
}
Bind the value of isFetching in state. Then update the state when the value change to make react re-render.

React does not render recursive reactive components

Given this component :
import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class SubscriptionView extends TrackerReact(Component) {
constructor(props) {
super(props);
let params = props.params || [];
if (!Array.isArray(params)) {
params = [params];
}
this.state = {
subscription: {
collection: Meteor.subscribe(props.subscription, ...params)
}
};
}
componentWillUnmount() {
this.state.subscription.collection.stop();
}
render() {
let loaded = this.state.subscription.collection.ready();
if (!loaded) {
return (
<section className="subscription-view">
<h3>Loading...</h3>
</section>
);
}
return (
<section className="subscription-view">
{ this.props.children }
</section>
);
}
};
And another component :
import SubscriptionView from './SubscriptionView.jsx';
export const Foo = () => (
<SubscriptionView subscription="allFoo">
<SubscriptionView subscription="singleBar" params={ 123 }>
<div>Rendered!</div>
</SubscriptionView>
</SubscriptionView>
);
The first Subscription is re-rendered when the data is available, however the second one is rendered only once and nothing more. If I place a console.log(this.props.subscription, ready); inside the render function of SubscriptionView, I see
allFoo false
allFoo true
singleBar false
and that's it.
On the server side, both publish methods are
Meteor.publish('allFoo', function () {
console.log("Subscribing foos");
return Foos.find();
});
Meteor.publish('singleBar', function (id) {
console.log("Subscribing bar", id);
return Bars.find({ _id: id });
});
Both of the publish methods are being called.
Why isn't the second SubscriptionView reactive?
* Solution *
This is based on alexi2's comment :
import React, { Component } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class SubscriptionLoader extends TrackerReact(Component) {
constructor(props) {
super(props);
let params = props.params || [];
if (!Array.isArray(params)) {
params = [params];
}
this.state = {
done: false,
subscription: {
collection: Meteor.subscribe(props.subscription, ...params)
}
};
}
componentWillUnmount() {
this.state.subscription.collection.stop();
}
componentDidUpdate() {
if (!this.state.done) {
this.setState({ done: true });
this.props.onReady && this.props.onReady();
}
}
render() {
let loaded = this.state.subscription.collection.ready();
if (!loaded) {
return (
<div>Loading...</div>
);
}
return null;
}
};
Then, inside the parent component's render method :
<section className="inventory-item-view">
<SubscriptionLoader subscription='singleBar' params={ this.props.id } onReady={ this.setReady.bind(this, 'barReady') } />
<SubscriptionLoader subscription='allFoos' onReady={ this.setReady.bind(this, 'foosReady') } />
{ content }
</section>
Where setReady merely sets the component's state, and content has a value only if this.state.barReady && this.state.foosReady is true.
It works!
Try separating out your SubscriptionView Components like this:
import SubscriptionView from './SubscriptionView.jsx';
export const Foo = () => (
<div>
<SubscriptionView subscription="singleBar" params={ 123 }>
<div>Rendered!</div>
</SubscriptionView>
<SubscriptionView subscription="allFoo">
<div>Rendered Again!</div>
</SubscriptionView>
</div>
);
Edit from comments conversation
Not sure if I am on the right track but you could build Foo as a 'smart' component that passes props to each SubscriptionView as required, and then use Foo as a reusable component.
Let's say that what I need to render is FooBarForm, which requires both Foos and Bars to be registered, in that specific use case. How would you do that?
You could create Components Foos and Bars that took props as required and create a parent component FooBarForm that contained those Components and passed the necessary data.
FooBarForm would handle the state and as that changed pass it to the props of its child components.
Now state is being centrally managed by the parent component, and the child components render using props passed from the parent.
The child components would re-render as their props changed depending on whether the state being passed from the parent component had changed.

Categories