I have written the following sample counter app using react.js. onClick fires the handleAddOne but the value of count never gets incremented.
class Counter extends React.Component {
constructor(props){
super(props);
this.handleAddOne = this.handleAddOne.bind(this);
this.state = {
count : 0
};
}
handleAddOne() {
console.log('handleAddOne');
this.state.count++;
}
render() {
return (
<div>
<h1>Count:{this.state.count}</h1>
<button onClick={this.handleAddOne}>+1</button>
</div>
);
}
}
ReactDOM.render(<Counter />, document.getElementById('app'));
In react, state is an immutable object, meaning you are supposed to set state values using only setStste function and not directly.
<button onClick={()=>{this.setState({count:this.state.count++})
}}>
+1
</button>
As the other answers suggest, you should be using setState to trigger the state change and rerender.
However, when using prevState it is also recommended that you use the functional version of setState
handleAddOne() {
console.log('handleAddOne');
this.setState(prevState=>({
count: prevState.count+1
}));
}
Documentation: https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly
I advise you to read more about Reactjs from https://reactjs.org/docs/hello-world.html
handleAddOne() {
console.log('handleAddOne');
this.setState({count: this.state.count++})
}
Related
I need to know when Hidden Element did mount.
I use ref to check it did mount and control this element.
And use componentDidUpdate to check when Hidden Element did mount.
But use componentDidUpdate in a big project, some elements often trigger componentDidUpdate.
I'm afraid the efficiency will be bad.
Is there another way for me to know when Hidden Element did mount?
Appreciate your help.
In addition, why I need to know it because I need to use a Radium package to build the animation.
When the 'someState' is true, I will auto play the animation for the element.
I use the style animation-play-state : 'running'.
This animation will break in the safari, but it is okay that users visit it for the first time.
When the users refresh safari and have a cache, the users visit it again causing the animation can't autoplay.
So I set animation-play-state : paused.
When I confirm the element did mount, I will use ref change animation-play-state to running.
I find an issue with this problem.
Link: https://github.com/FormidableLabs/radium/issues/912
My sudo code.
import React from "react";
const initialState = {
someState: false
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = initialState;
this.hiddenElement = null;
}
componentDidMount() {
console.log("componentDidMount");
}
componentDidUpdate() {
console.log("componentDidUpdate");
if (this.hiddenElement !== null) console.log("hiddenElement did Mount");
// I will change the properties of this.hiddenElement, or others in the next steps.
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>
Click Me
</button>
{someState && (
<div ref={r => (this.hiddenElement = r)}>Hidden Element</div>
)}
</div>
);
}
}
export default App;
Okay, so if you're using an external package and need to manipulate the element based on a state change, then you'll have to basically use componentDidUpdate().
Only worry about the performance when it becomes a bottleneck (after profiling things, etc.).
Since setting a ref doesn't cause componentDidUpdate (it's not a bit of state), you may wish to refactor the animation-mutating method to something like this (note how the update...() method is called in the ref callback).
class App extends React.Component {
constructor(props) {
super(props);
this.state = { someState: false };
this.hiddenElement = null;
}
updateElementAnimation() {
if (!this.hiddenElement) return; // not mounted yet
if (this.state.someState) {
this.hiddenElement.something();
} else {
this.hiddenElement.somethingElse();
}
}
componentDidUpdate() {
this.updateElementAnimation();
}
render() {
const { someState } = this.state;
return (
<div className="App">
<button onClick={() => this.setState({ someState: true })}>Click Me</button>
{someState && (
<div
ref={r => {
this.hiddenElement = r;
this.updateElementAnimation();
}}
>
Hidden Element
</div>
)}
</div>
);
}
}
You can check inside your componentDidUpdate the value of your someState. If this is true, then you are sure that the element you need is rendered, as componentDidUpdate is invoked after an update occurs (thus after the render method).
...
componentDidUpdate() {
if (this.state.someState) {
// your element is rendered, do what you need
}
}
...
I'm trying to build a component with auto-updating value based on cookies:
let cookies = 0;
(function count() {
cookies = document.cookie.split("?");
setTimeout(count, 10);
return cookies;
})();
class CartButton extends React.Component {
state = {quantity: cookies.length}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>
);
}
}
'count' function works as expected, component is rendered with the latest value returned. Unfortunately, it does not auto-update when 'cookies' are changed. It returns this error:
Warning: render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render.
I have tried various variations here but still can't figure it out :/
componentDidMount will get execute only once when your component loads first time. This is the correct place to write any logic which we need to execute after page load.
Try this,
class CartButton extends React.Component {
//It is good to have a constructor for the component which has state
constructor(props){
super(props);
this.state = {quantity: cookies.length}
this.updateQuantity;
}
componentDidMount(){
this.updateQuantity = setInterval(()=> {
cookies = document.cookie.split("?");
this.setState({quantity: cookies.length})
},10)
}
//Don't forget to clear any setInterval like below
componentWillUnmount(){
clearInterval(this.updateQuantity);
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
Here your CartButton is not updating even though count is working fine because CartButton is not listening to your cookies variable. React component updates only when there is either props or state change.
You can something like this..
class CartButton extends React.Component {
state = {quantity: cookies.length}
componentDidMount(){
setInterval(function count() {
cookies = document.cookie.split("?");
this.setState({quantity: cookies})
}.bind(this), 10)
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
I am working on project , where I am updating the url . Actually It working fine when I click on next button but I want to re-render application when function is invoked . I am new to ReactJS , Could some one please help me how to solve this problem .
I am sorry I am not native speaker , I am sorry if I made mistake in English grammer . Thanks
Displaying data through this URL Function
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&
filter[skip]=${this.state.skip}
&&filter[order]=${this.state.sortedData}`;
}
Full Component Code
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))
You can force a rerender of your component by calling:
this.forceUpdate();
However, you have to be careful not to enter a never-ending update cycle.
The usual (recommended) way to rerender is through the necessity of changing your state using setState.
EDIT
I think want you need is to call getData once you have clicked the table header:
getSortedData=()=>{
console.log("hello clicked")
this.setState({
sortedData:'companyName'
})
// now invoke getdata which will refresh the data:
this.getData()
}
I have a react component, which has properties and state. Some fields of state contain input data (uplifted from input control), but there is also fields in the state that must be Calculated based on current State and Props:
The question: what is the best way to update calculated fields of the state (based on other fields of state and props)?
Ugly way to do it:
componentDidUpdate(){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
In this case I get infinite loop of updates or in the best case (if I use PureComponent) double rendering invocation.
The best solution I found so far (but still ugly):
Is to create a calculated object in state, which contains calculated fields and updated in componentWillUpdate avoiding setState:
componentWillUpdate(nextProps,nextState){
nextState.calculated.field1=f(nextProps,nextState)
}
class ParentComponent extends React.Component {
constructor(props, ctx) {
super(props,ctx)
this.state={A:"2"}
}
render() {
console.log("rendering ParentComponent")
return <div>
<label>A=<input value={this.state.A} onChange={e=>{this.setState({A:e.target.value})}} /></label> (stored in state of Parent component)
<ChildComponent A={this.state.A} />
</div>
}
}
class ChildComponent extends React.PureComponent {
constructor(props,ctx) {
super(props,ctx);
this.state={
B:"3",
Calculated:{}
}
}
render() {
console.log("rendering ChildComponent")
return <div>
<label>B=<input value={this.state.B} onChange={e=>{this.setState({B:e.target.value})}} /></label> (stored in state of Child component state)
<div>
f(A,B)=<b>{this.state.Calculated.result||""}</b>(stored in state of Child component)
<button onClick={e=>{ this.setState({Calculated:{result:new Date().toTimeString()}}) }}>Set manual value</button>
</div>
</div>
}
componentWillUpdate(nextProps, nextState) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, nextState.B)
}
componentWillReceiveProps(nextProps) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, this.state.B)
}
componentWillMount() {
this.state.Calculated.result = getCalculatedResult(this.props.A, this.state.B)
}
}
function getCalculatedResult(a,b) {
const aNum = Number(a)||0
const bNum = Number(b)||0;
const result = (aNum*bNum).toString();
return result;
}
ReactDOM.render(<ParentComponent/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
This is also ugly solution and React does not recommended to mutate state avoiding setState. So what is right solution for that?
NOTE:
In my real application I cannot recalculate f(a,b) every single time during rendering, because it's actually complex object, so I need to cache it somehow and the best way is in the state.
If you are using React 16.8.0 and above, you can use React hooks API. I think it's useMemo() hook you might need. For example:
import React, { useMemo } from 'react'
const MyComponent = ({ ...props }) => {
const calculatedValue = useMemo(
() => {
// Do expensive calculation and return.
},
[a, b]
)
return (
<div>
{ calculatedValue }
</div>
)
}
For more details, refer to the React documentation
I wouldn't advice you to store your calculated value inside your state. My approach would be more like this:
import PropTypes from 'prop-types';
import React from 'react';
class Component extends React.Component {
static defaultProps = { value: 0 };
static propTypes = { value: PropTypes.number };
state = { a: 0, b: 0 };
result = () => this.state.a + this.state.b + this.props.value;
updateA = e => this.setState({ a: +e.target.value });
updateB = e => this.setState({ b: +e.target.value });
render() {
return (
<div>
A: <input onChange={this.updateA} value={this.state.a} />
B: <input onChange={this.updateB} value={this.state.b} />
Result: {this.result()}
</div>
);
}
}
The problem with storing the calculation inside your state is, that the calculation can be mutated by multiple sources. If you use my solution, there is no way, that anything can overwrite the calculation WITHOUT using the correct function to calculate them.
You can save calculated result in this.calculated instead of this.state. It is dependent data. All data which causes update and render is already in state and props.
class Component extends React.Component {
constructor(props) {
super(props)
state = {
b: 0
}
}
updateThis = (event) => {
this.setState({ b: event.target.value });
}
componentWillUpdate(nextProps,nextState){
this.calculated.field1=f(nextProps.a, nextState.b)
}
render() {
return (
<form>
A = <input onChange={this.props.updateParent} value={this.props.a} /> <br>
B = <input onChange={this.updateThis} value={this.state.b} /> <br>
f(A,B) = {this.calculated.field1} <br>
</form>
);
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props)
state = {
a: 0
}
}
render() {
return (
<Component
updateParent={event=>this.setState({a: event.target.value})}
a={this.state.a}
/>
}
}
}
You're first attempt is the right way to solve this problem. However, you need to add a check to see if state has actually changed:
componentDidUpdate(prevProps, prevState){
if(prevState.field !== this.state.field){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.calculatedField !== nextState.calculatedField
}
You need to check the pieces of state and props that you use in your calculate method and make sure they have changed before updating state again. This will prevent the infinite loop.
It looks like the "state" is the place to store everything (even computed values) you'll need to use on the render function, but usually we have the problem you describe.
Since React 16.3 a new approach for this situation has been given in the way of the static getDerivedStateFromProps (nextProps, prevState) "lifecycle hook".
You should update at least to this version if you haven't, and follow the advice given by the React Team on their blog.
Here is the official documentation for this functionality.
The issue here is that this function is invoked before every render, and being "static" you cannot access the current instance previous props, which is usually needed to decide if the computed value must be generated again or not (I suppose this is your case, as you have stated your computation process is heavy). In this case, the React team suggests to store in the state the values of the related props, so they can be compared with the new ones:
if (nextProps.propToCompute !== prevState.propToComputePrevValue) {
return {
computedValue: Compute(nextProp.propToCompute),
propToComputePrevValue: nextProps.propToCompute
};
}
return null;
Do not include redundant information in your state.
A simplified example is having firstName and lastName in your state. If we want to display the full name in your render method, you would simply do:
render() {
return <span>{`${this.state.firstName} ${this.state.lastName}`}</span>
}
I like this example because it's easy to see that adding a fullName in our state, that just holds ${this.state.firstName} ${this.state.lastName} is unnecessary. We do string concatenation every time our component renders, and we're okay with that because it's a cheap operation.
In your example, your calculation is cheap so you should do it in the render method as well.
I have some problem with splice() method in my React.js app.
So, this is an example app. Deletion not works now. What's wrong here? Part of code:
class CardList extends React.Component {
static propTypes = {
students: React.PropTypes.array.isRequired
};
// ADD DELETE FUNCTION
deletePerson(person) {
this.props.students.splice(this.props.students.indexOf(person), 1)
this.setState()
}
render() {
let that = this
return <div id='list'>
{this.props.students.map((person) => {
return <Card
onClick={that.deletePerson.bind(null, person)}
name={person.name}>
</Card>
})}
</div>
}
}
class Card extends React.Component {
render() {
return <div className='card'>
<p>{this.props.name}</p>
{/* ADD DELETE BUTTON */}
<button onClick={this.props.onClick}>Delete</button>
</div>
}
}
http://codepen.io/azat-io/pen/Vaxyjv
Your problem is that when you call
onClick={that.deletePerson.bind(null, person)}
You bind this value to null. So inside of your deletePerson function this is null instead of actual component. You should change it to
onClick={that.deletePerson.bind(this, person)}
And everything would work as expected =)
Changing the bind value to this will definitely cause the call to this.setState() to work, thus triggering the re-render, however I strongly recommend against the approach you've taken.
Props are supposed to be immutable. Instead use internal state and replace with new values rather than mutate them. To do this, set the state of your component in the constructor by doing something like:
constructor(props) {
super(props)
this.state = {
students: ...this.props.students
}
}
And now when you need to delete a person:
deletePerson(person) {
// notice the use of slice vs splice
var newStudents = this.props.students.slice(this.props.students.indexOf(person), 1)
this.setState({ students: newStudents })
}
And finally use this.state.students in your render method instead.
The reasoning behind this is that props are passed directly from the parent container component so modifying them wouldn't really make sense. To make more sense of my own code, I tend to pass in the prop named initialStudents and set my state to students: ...initialStudents to ensure I make the distinction between my prop variable and my state variable.