As title.
What would I do to change state inside a callback function if I need to change state after I got some results from the back-end or files ,etc...?
Like this:
var strView="";
var CountIsPrime=function(InputNum){
IsPrime(InputNum,function(Res){
strView=Res;
});
};
export class TrialClass extends React.Component{
state={
DisplayString:strView
};
render(){
return <div>
<label>{strView}</label>
<button onclick={()=>CountIsPrime(Math.floor(Math.Random()*10000))}></button>
</div>
}
}
I am wondering how to change the value inside the label when I call CountIsPrime function?
Issues
strView isn't part of any React component state or props, so no amount of updating it will trigger a rerender to display updated values.
onclick might've been a typo, but it isn't valid.
Solution
Move the CountIsPrime callback definition into the component so it can update the state.
Render the state value into the label.
Use onClick handler.
Code
export class TrialClass extends React.Component{
state = {
strView: '',
};
countInPrime = inputNum => IsPrime(
inputNum,
strView => this.setState({ strView }),
);
render() {
const { strView } = this.state;
return (
<div>
<label>{strView}</label>
<button
type="button"
onClick={() => this.countInPrime(Math.floor(Math.Random() * 10000))}
>
X
</button>
</div>
);
}
}
Related
I have passed a data model as props from Parent Component (MinorStructures) to Child component (PhotoGallery).
The parent Component looks like the following:
export default class MinorStructures extends Component {
constructor(props)
{
super(props);
// This is a super data model class, its main function is to collect
// data from all the children components.
this.state = {
MinorStructures: {
layer: 3,
layerName: 'MinorStructures',
layerId: -1,
agencyId: -1, //to be determined later
galleryModel:{
selectedFile: null
}
}
};
this.panes = [
{
menuItem: 'Photo Gallery', render: () =>
<Tab.Pane>
<PhotoGallery triggerNeedSave={this.props.triggerNeedSave}
disabled={this.props.disabled}
loggedIn = {this.props.loggedin}
minorModel={this.state.MinorStructures}/>
</Tab.Pane>
},
];
}
}
I have removed few codes from the parent class which is not necessary for this problem.
The Child Component is like the following:
export default class PhotoGallery extends Component{
constructor(props) {
super(props)
const {minorModel} = this.props
this.state={
cameraOpen: false,
photoModel: minorModel.galleryModel
}
console.log("Constructor State ", this.state)
}
handleChange = e =>{
this.props.triggerNeedSave();
this.setState({[photoModel.selectedFile]:e.target.files[0]})
console.log(this.state)
}
render() {
const uploadClick = e => {
hiddenFileInput.current.click();
};
return (
<div>
{!this.state.cameraOpen && <Button size='tiny' onClick={uploadClick}
color='brown'
disabled ={this.props.disabled}>Upload Photos from Device</Button>}
<input id="photo" name="selectedFile" type="file"
onChange={this.handleChange}
ref={hiddenFileInput} style={{display:'none'}} />
<Button size='tiny' onClick={checkModel} color='brown'
disabled ={this.props.disabled}>
Click To Check
</Button>
</div>
);
};
return (
<div id="root">
<Gallery />
</div>
)}
}
In the state of PhotoGallery class I have a photoModel that takes the data model from MinorStructures as props. When I select a picture and do setState in the handleChange method of PhotoGallery class it says photoModel is not defined. But I have defined that variable in the state which stores data model passed as props from MinorStructures.
You are using dynamic keys when changing state with [] to access properties on this.state
this.setState({[photoModel.selectedFile]:e.target.files[0]})
The correct way would be to change state with
this.setState({photoModel.selectedFile:e.target.files[0]})
I set a name attribute for the file type input html tag and then used the below code to set the value
this.setState({ [name]: e.target.files[0] });
I used ternary syntax for the two way bind properties val and initialName,
I've rendered the datas into my button tag which is having the OnClick function
class App extends Component{
constructor() {
super();
this.state = {
val:'Initial value',
initialName : 'JonSnow'
};
}
onClick = () => {
const initialName = this.state.initialName
=== 'JonSnow' ? 'Jax' : 'JonSnow';
const val = this.state.val ===
'Initial Value' ? 'Changed' : 'Initial Value';
return this.setState ( {initialName},{val});
}
render() {
return (
<div className='App'>
<h1>Hai there ! </h1>
<p>I am a react paragraph </p>
<button onClick={this.onClick}>Click Me</button>
<Program initialName={this.state.initialName}/>
<Program val= {this.state.val}/>
</div>
);
}
this.setState accepts an object that will be merged with the current state.
So if you want to override two values, just pass those both in one object:
this.setState ( {initialName,val});
You do not need to return the setState function call, sicne it will be discarded anyway.
The second parameter of the setState is a callback to be executed once the state is updated.
this.setState ( {initialName,val}, () => {THIS WILL BE CALLED ONCE THE STATE IS UPDATED});
I want to setsState to make it re-render. Because if i click on one on the button, it doesnt change color until the second click.
import React from 'react';
class SessionForms extends React.Component {
constructor(props) {
super(props);
this.state = {colorRed: 0};
this.loginRed = this.loginRed.bind(this);
this.signupRed = this.signupRed.bind(this);
}
loginRed(e) {
e.preventDefault();
this.setState({colorRed: 1})
this.props.openModal('login');
const login = document.getElementById("session-button-login");
const signup = document.getElementById("session-button-signup");
login.classList.add("red-button");
signup.classList.add("gray");
}
signupRed(e) {
e.preventDefault();
this.setState({colorRed: 2})
this.props.openModal('signup')
const login = document.getElementById("session-button-login");
const signup = document.getElementById("session-button-signup");
signup.classList.add("red-button");
login.classList.add("gray");
}
render() {
return (
<div className='session'>
<div>
<button className='session-close'
onClick={() => this.props.closeModal()}
>X</button>
</div>
<div className='session-type'>
<div></div>
<button
className='session-type-button'
id='session-button-login'
onClick={this.loginRed}>
LOG IN
</button>
<button
className='session-type-button'
id='session-button-signup'
onClick={this.signupRed}>
JOIN
</button>
</div>
</div>
);
}
};
export default SessionForms;
setState() does asynchronous operation. You can call a function after the state value has updated.
this.setState({colorRed: 1},()=>{
console.log(this.state.colorRed});
})
calling callback you can check there value of state is updated.
for better understanding of setState(). refer to React setState is asynchronous!
replace changing states to the bottom of your methods
signupRed(e) {
e.preventDefault();
// this.setState({colorRed: 2}) // <--- from
this.props.openModal('signup')
const login = document.getElementById("session-button-login");
const signup = document.getElementById("session-button-signup");
signup.classList.add("red-button");
login.classList.add("gray");
// it rerenders component after changes above
this.setState({colorRed: 2}) // <--- to
}
But as mentioned in comments you should use react approach to work with DOM (ref, className, style etc.): start with this article: https://reactjs.org/docs/faq-styling.html
I am learning reactjs and I wrote component with the method componentWillReceiveProps (cWRP) but I read that it is deprecated and it must replace with getDerivedStateFromProps (gDSFP) - https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html.
Please note that the following code has the sole purpose of illustrating my problem and questions. It is not a full code.
App.js file :
import React from 'react';
import './App.css';
import Display from './component.js'
class App extends React.Component {
state={resetCounter:false}
resetCounter= () => this.setState( {resetCounter: true} );
render() {
return (
<div className="App">
<header className="App-header">
<Display resetCounter={this.state.resetCounter}></Display>
<div>
<p></p><p></p>
<button onClick={this.resetCounter}>Reset</button>
</div>
</header>
</div>
);
}
componentDidUpdate () {
if (this.state.resetCounter!==false)
this.setState( {resetCounter: false} );
}
}
export default App;
component.js file
import React from 'react'
class Display extends React.Component {
constructor() {
super();
this.state = this.resetState();
this.state.generalCounter=0;
}
/* method to avoid code duplication in constructor and cWRP
could not be used with getDerivedStateFromProps */
resetState = () => ({resettableCounter: 0,});
componentWillReceiveProps(nextProps) {
if (nextProps.resetCounter===true)
this.setState(this.resetState())
}
render() {
return (
<>
<div>
<div>general counter : {this.state.generalCounter}</div>
<div>resettable counter : {this.state.resettableCounter}</div>
</div>
<div>
<button onClick={this.incCounters}>+</button>
<button onClick={this.decCounters}>-</button>
</div>
</>
)
}
incCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter+1,
generalCounter: this.state.generalCounter+1
}
)
decCounters= () => this.setState(
{
resettableCounter: this.state.resettableCounter-1,
generalCounter: this.state.generalCounter-1
}
)
}
export default Display
In the state of the component, there is a resettable part and a non resettable one. A method resetState is used to avoid code duplication in the constructor and in cWRP.
To replace cWRP by gDSFP, I wrote a class method because instance method could NOT be called in gDSFP (this is not usable)
...
constructor() {
super();
this.state = Display.resetState();
this.state.generalCounter=0;
}
static resetState () {
return ({resettableCounter: 0,});
}
static getDerivedStateFromProps(nextProps) {
if (nextProps.resetCounter === true) {
return Display.resetState();
} else {
return null;
}
}
...
With this solution, it is very easy to modify all my components but I am not sure that it is a good mean.
I wonder if I have a misconception and if I should rewrite my components to separate them into Fully controlled components and Fully uncontrolled components with a key ( https://en.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions).
For example, in this case, do I have to write :
One Fully uncontrolled components for the resettable counter
One Fully controlled one for the non resettable counter
A parent component with the +/- buttons to render them.
I ask this question because in some cases, it will be much work, so I want to be sure before continuing.
You would want to keep the gdsfp version in your post if your component depends on some outside props, which you don't have controll over (such as JSON returned or 3rd party render props component, etc).
It looks like you have a full control over what's passed down to the Display. You can pass down an initial resettableCounter value down to Display.
The advantage is two-folds.
Your Display props shows what the Display does - Making it more descriptivie/readable.
It's easier to maintain, as you don't have to massage the data.
For your particular case, Fully uncontrolled component with a key seems to make more sense, as Display should accept the initial value to show, but is responsible for managing the reseetableCounter.
Unless it's absolutely unavoidable, don't create components which control their siblings (or parents). Instead, lift state to a common ancestor:
const Display = ({
generalCounter,
resettableCounter,
incrementCounters,
decrementCounters,
}) => (
<div>
<div>General Counter: {generalCounter}</div>
<div>Resettable Counter: {resettableCounter}</div>
<button onClick={incrementCounters}>Increment</button>
<button onClick={decrementCounters}>Decrement</button>
</div>
);
class DisplayContainer extends React.Component {
state = {
generalCounter: 0,
resettableCounter: 0,
};
incrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter + 1,
resettableCounter: prevState.resettableCounter + 1,
}));
decrementCounters = () => this.setState(prevState => ({
generalCounter: prevState.generalCounter - 1,
resettableCounter: prevState.resettableCounter - 1,
}));
resetResettableCounter = () => this.setState({
resettableCounter: 0,
});
render() {
return (
<React.Fragment>
<Display
{...this.state}
incrementCounters={this.incrementCounters}
decrementCounters={this.decrementCounters}
/>
<button onClick={this.resetResettableCounter}>
Reset Resettable Counter
</button>
</React.Fragment>
);
}
}
const App = () => (
<div>
<DisplayContainer />
</div>
);
An alternative approach would be something like Redux (which effectively lifts state out of React).
I have this container where and is not placed in the same level. How can I get the state of the Form when I click on the button (which is placed on the parent) ?
I've created a demo to address my issue.
https://codesandbox.io/s/kmqw47p8x7
class App extends React.Component {
constructor(props) {
super(props);
}
save = () => {
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form />
<button onClick={this.save}>save</button>
</div>
);
}
}
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
To access a child instance from parent, your need to know about ref:
First, add formRef at top your App class:
formRef = React.createRef();
Then in App render, pass ref prop to your Form tag:
<Form ref={this.formRef} />
Finaly, get state from child form:
save = () => {
alert("how to get state of Form?");
const form = this.formRef.current;
console.log(form.state)
};
Checkout demo here
ideally, your form submit action belongs to the Form component
You can put button inside your From component and pass a submit callback to the form.
class App extends React.Component {
constructor(props) {
super(props);
}
save = (data) => {
// data is passed by Form component
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form onFormSubmit={this.save} />
</div>
);
}
}
you can write the code like this
https://codesandbox.io/s/23o469kyx0
As it was mentioned, a ref can be used to get stateful component instance and access the state, but this breaks encapsulation:
<Form ref={this.formRef}/>
A more preferable way is to refactor Form to handle this case, i.e. accept onChange callback prop that would be triggered on form state changes:
<Form onChange={this.onFormChange}/>
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
Forms will need to handle this any way; it would be impossible to reach nested form with a ref from a grandparent. This could be the case for lifting the state up.
E.g. in parent component:
state = {
formState: {}
};
onFormChange = (formState) => {
this.setState(state => ({
formState: { ...state.formState, ...formState }
}));
}
render() {
return (
<Form state={this.state.formState} onChange={this.onFormChange} />
);
}
In form component:
handleChange = e =>
this.props.onChange({
[e.target.name]: e.target.value
});
render() {
return (
<input
onChange={this.handleChange}
name="firstName"
value={this.props.state.firstName}
/>
);
}
Here is a demo.