I am a beginner in React. I am trying to do a counter for a fibonacci sequence. For those who don't know what is a fibonacci sequence, here is a guide. It works at the beginning and then it starts to go off sequence. I want to be able display the number in the fibonacci sequence and move up and down the sequence. Here is what my code so far:
import React, { Component } from 'react';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
previous1: 1,
previous2: 1,
count: 1
}
this.clickDecrease = this.clickDecrease.bind(this);
this.clickIncrease = this.clickIncrease.bind(this);
}
clickIncrease(e) {
this.setState({
previous1: this.state.count,
previous2: this.state.previous1,
count: this.state.previous1 + this.state.previous2
});
}
clickDecrease(e) {
this.setState({
previous1: this.state.previous2,
previous2: this.state.count - this.state.previous1,
count: this.state.previous1
});
}
render() {
return (
<div>
The current number is: {this.state.count}
<button onClick={this.clickDecrease} className="btn btn-danger">-</button>
<button onClick={this.clickIncrease} className="btn btn-primary">+</button>
</div>
);
}
}
I tried to store the last two numbers in a property of the state so I can add them up to get the next sequence. However, it does not seems to work. What should I do to make this better?
Check the working code, you don't need a separate count variable to store the series value, previous1 will always have that value.
How to update series values:
During '+' : At each step previous1 will be equals to (previous1 + previous2) and previous2 will take the previous1 value.
During '-': previous1 will become prevoious2 and previous2 will take the value of previous1 - previous2
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
previous1: 1,
previous2: 0,
}
this.clickDecrease = this.clickDecrease.bind(this);
this.clickIncrease = this.clickIncrease.bind(this);
}
clickIncrease(e) {
let a = this.state.previous1 + this.state.previous2;
this.setState({
previous1: a,
previous2: this.state.previous1,
});
}
clickDecrease(e) {
this.setState({
previous1: this.state.previous2,
previous2: this.state.previous1 - this.state.previous2,
});
}
render() {
return (
<div>
The current number is: {this.state.previous1}
<br/>
<button onClick={this.clickDecrease} className="btn btn-danger">-</button>
<button onClick={this.clickIncrease} className="btn btn-primary">+</button>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id ='app'/>
Note: Put the condition in decrease function, when the value of state variable will be 1 and 0, otherwise it will take the negative values.
If you use React hooks, things can be quite simple. Use a ref to hold the previous count, when the current count changed, add the previous count and the current count together. that's all.
Here I moved hold the previous count into the usePrevious hook.
const App = () => {
const [count, setCount] = useState(1);
const previousCount = usePrevious(count); // hold the previous count
const addCount = () => {
setCount(count + previousCount);
};
return (
<div>
<div>previousCount: {previousCount}</div>
<div>count: {count}</div>
<button onClick={addCount}>click me</button>
</div>
);
}
usePrevious.js:
const usePrevious = value => {
const ref = useRef(value);
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
export default usePrevious;
Related
Why when I am doing this.setState({count:this.state.count*2}) it is working, but when I am doing: this.setState({count:this.state.count++}) it is not working?
Why, and how to fix it?
Full code:
var Hello = React.createClass({
getInitialState:function(){
return {count:parseInt(this.props.count)}
},
a:function(){
this.setState({count:this.state.count++})
console.log(this.state)
},
render: function() {
console.log(this.state)
return <div onClick={this.a}>Click to increment the counter<b> {this.state.count} </b></div>;
}
});
ReactDOM.render(
<Hello count="1" />,
document.getElementById('container')
);
But this code is working:
a:function(){
this.setState({count:this.state.count*2})
console.log(this.state)
},
JSFiddle: https://jsfiddle.net/69z2wepo/55100/
setState is an async function. React may batch a bunch of setStates together.
So the value of this.state.count is the value at the time you make the request.
A better solutions to call a function that gets evaluated at the time the setState gets executed.
this.setState((prevState, props) => ({
counter: prevState.counter + 1
}));
from https://facebook.github.io/react/docs/state-and-lifecycle.html
By doing this.state.count++, you mutate the state, because it's the same thing than doing this.state.count += 1. You should never mutate the state (see https://facebook.github.io/react/docs/component-api.html). Prefer to do that instead:
this.setState({ count: this.state.count + 1 })
The setState function returns before this.state.count is incremented because you are using a post-fix operator (++). Also, setState is asynchronous, it accepts a callback as the second argument which get triggered when the state has been updated, so you should put your console.log inside of the cb.
You are trying to mutate state (access this.state.field and increase its value) that's what ++ is. It tries to increase that state value, and then assign it to new state :) Only ever mutate state by calling setState.
Try
this.setState({count: this.state.count+1})
or
this.setState({(state)=>({count: state.count + 1})}
// new state variable inside function's scope, we can play with that, ++ even safely. but don't call ++ on this.state ever.
In general, don't use ++, it's bad practice.
For simple assignments
a+=1 || a-=1 || a*=1 || a%=1
are better, or even write them explicitly.
a = a + 1
We can use the above snippet for counter increment, decrement and reset when an event occurs.
import { useState } from "react";
const Count = () => {
const [num, setNum] = useState(0);
const CounterI = () => {
setNum (num+1);
}
const CounterD = () => {
setNum (num-1);
}
const ResetCounter = () =>{
setNum (0);
}
return(
<div>
<p>Count Value: {num}</p>
<button onClick={CounterI}>Counter Increment</button>
<button onClick={CounterD}>Counter Decrement</button>
<button onClick ={ResetCounter} >Reset</button>
</div>
);
}
export default Count;
You can bind that using bind(this) function
eg:-
<div onClick={this.increase.bind(this)}>
or you can use Arrow function that will bind for you
eg:-
<div onClick={()=> this.increase()} >
Another reason is because "something++" does MUTATE the value, but return the OLD VALUE. "++something" also MUTATE +1 the same way, but return the NEW VALUE
a = 3
console.log(a++) // Will log out the OLD VALUE 3, but in memory `a` will be mutated into 4
console.log(a++) // Will log out OLD VALUE 4, but in memory `a` will be mutated into 5
console.log(++a) // Will log out 6, since 5+1=6 is the new value
But all in all, do not mutate states in React
hope this helps for you
import { render } from '#testing-library/react';
import React, { Component } from 'react';
class Counter extends Component{
state = {
count: 0,
};
handleIncrement = () => {
this.setState({ count: this.state.count + 1 })
};
handleDecrement = () => {
this.setState({ count: this.state.count - 1 })
};
render(){
return(
<div>
<button onClick={this.handleIncrement} className="btn btn-secondary btn-sm m-4 ">Increment</button>
<span className = "badge bg-primary m-5">{this.state.count}</span>
<button onClick={this.handleDecrement} className="btn btn-secondary btn-sm ">Decrement</button>
</div>
)
}
};
export default Counter;
I found an solution. When I am doing this.setState({count:++this.state.count}) it is working.
The reason is when I am doing this.setState({count:this.state.count++}) the new state.count value not being sent to the setState React function.
This should be pretty simple, but I can't figure out how to do it.
I have a component with multiple buttons, each with a "count" value, set with state. When a user clicks, the count goes up.
Right now, when I click one of the buttons, both counters change. How can I make it so only the div that was clicked updates, using the same state?
Edit: I don't want to have different counts, as I'd like for this component to render buttons dynamically. What if I don't know how many buttons I'll have at first?
class Block extends React.Component {
state = {
count: 0
};
handleClick = e => {
const count = this.state.count;
this.setState({ count: count + 1 });
};
render() {
return (
<div>
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
</div>
);
}
}
This is more of an issue of learning how to think in react.
If you need to be able to reuse a piece of functionality like a counter, you can make it its own component and have it manage its own state. Then you can reuse it wherever you need.
Here's an example:
class Counter extends React.Component {
state = {
count: 0
};
handleClick = () => {
// Use updater function when new state is derived from old
this.setState(prev => ({ count: prev.count + 1 }));
};
render() {
return (
<button className="block" onClick={this.handleClick}>
<div className="counter">{this.state.count}</div>
</button>
);
}
}
// Now you can use it dynamically like this:
class Block extends React.Component {
render() {
return (
<div>
<div>There are 4 counter component instances that each manage their own state.</div>
{[1,2,3,4].map(v => <Counter />)}
</div>
);
}
}
ReactDOM.render(<Block />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
you should define two state and when press each button update the current state and you can render the current state in the dome like this
state = {
firstCount: 0,
secondCount: 0
}
and write your action (function) to handle update state like this
handleUpdateCount = stateName => {
this.setState({
[stateName]= this.state[stateName] + 1
})
}
then you should called this function like this =>
this.handleUpdateCount('firstCount')
If your buttons are dynamic you can set your state to be an array and update the relevant index
class Block extends React.Component {
state = [];
handleClick = index => {
this.setState(state => {
const newState = [...state]; //keep state immutable
!newState[index] && (newState[index] = 0)
newState[index]++
return newState
});
};
render() {
return (
<div>
{[1,2,3].map((value, index) => <button className="block" onClick={() => this.handleClick(index)}>
<div className="counter">{this.state[index]}</div>
</button>)}
</div>
);
}
}
You have to use another value to update function when new state is derived from old state (like increment)
import React, { Component } from 'react'
export class Ref3 extends Component {
constructor(props) {
super(props)
this.state = {
count:0
}
}
//use prevState to help you update the old value to a new one
clickHandler=()=>{
this.setState((prevState=>({
count:prevState.count+1
})))
}
render() {
return (
<div>
<button onClick={this.clickHandler}>Click To Count</button>
{this.state.count}
</div>
)
}
}
export default Ref3
The question is probably rather unclear, but i did not how to formulate it, maybe that was the reason why i was not able to find solution to this puzzle i have. anyway, here is an example of what i want to accomplish:
<Calendar
tileContent={({ activeStartDate, date, view }) =>
this.renderGames(date, view)
}
/>
This is an example from npm package react-calendar, but i am sure you know what i mean. The param tileContent gets passed function that already has destructured object, and then i run my own function with data i get from that function.
I was thinking that this was done by executing function in child where i would pass an object (or single param, i just use object as an example).
I think what you're looking for are Render Props, not just executing function in parent with args (even though render props do this as well). It would appear your example is using Render Props specifically.
There are some good examples online of using render props in React, also referred to as "Wrapper Components", etc..
An example could be something like:
const { render } = ReactDOM;
class CounterWrapper extends React.Component {
state = {
count: 0
};
increment = () => {
const { count } = this.state;
return this.setState({ count: count + 1 });
};
decrement = () => {
const { count } = this.state;
return this.setState({ count: count - 1 });
};
render() {
const { count } = this.state;
return (
<React.Fragment>
{this.props.wrapperContent({
increment: this.increment,
decrement: this.decrement,
count
})}
</React.Fragment>
);
}
}
class App extends React.Component {
renderApp = (cnt, inc, dec) => {
return (
<div>
<h1>Render Props Counter Example</h1>
<div>
<p>{cnt}</p>
<button type="button" onClick={() => inc()}>
Increment
</button>
<button type="button" onClick={() => dec()}>
Decrement
</button>
</div>
</div>
)
};
render() {
return (
<CounterWrapper
wrapperContent={({ count, increment, decrement }) =>
this.renderApp(count, increment, decrement)
}
/>
);
}
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
It sounds like you want to execute a function that's in the parent component, from a child component with arguments passed from the child.
Here is an example:
const ParentComponent = () => {
const handleClick = (args) => {
console.log(args)
}
return (
<div>
<ChildComponent onClick={handleClick} />
</div>
)
}
const ChildComponent = ({onClick}) => {
const val = 5;
return (
<div>
<button onClick={() => handleClick(val)} name="Click">Click Me</button>
</div>
)
}
This hsould render the child component which is just a button, with an event handler that is sent from the parent. When you click the button, you should get a console log of 5, which is coming from the parent. This is how you would propgate values from the child, up to the parent.
I have an app component that takes one nested component. The nested component returns a number of buttons determined by one of its local state variable's lengths. Each button runs a programmatic this.setState() function to show a new set of data onClick. Here's the code described, and my question below:
class App extends React.Component {
render() {
return (
<div className='container'>
<Buttons />
</div>
)
}
}
class Buttons extends React.Component {
state = {
variableState,
count: 0,
chosen: 0,
}
upOne = x => {
this.setState(prevState => ({
count: prevState.count + 1,
chosen: x,
}))
console.log('running')
}
componentDidUpdate() {
console.log('componentupdated')
}
render() {
const {variableState, count, chosen} = this.state
const {upOne} = this
return (
<div>
{
variableState[count].answers.map((s, t) => <button onClick={() => upOne(t + 1)}>{s}</button>)
}
</div>
)
}
}
const variableState = [
{
answers: [
'one',
'two',
'three',
'four',
]
}
]
ReactDOM.render(<App />, document.getElementById('app'))
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
I want to update the <Buttons /> state by incrementing the count by one each time one of the buttons is clicked. This should run setState() which should update the component and run the componentDidUpdate() function. The problem is, the upOne() function runs, but it isn't updating the component and therefore not running the componentDidUpdate() function and I don't know why.
If I get rid of the Array.map() logic and make it a static function like this:
class Buttons extends React.Component {
state = {
variableState,
count: 0,
chosen: 0,
}
upOne = x => {
this.setState(prevState => ({
count: prevState.count + 1,
chosen: x,
}))
console.log('running')
}
componentDidUpdate() {
console.log('componentupdated')
}
render() {
const {variableState, count, chosen} = this.state
const {upOne} = this
return (
<button onClick={() => upOne(1)}>click</button>
)
}
}
It works as I expect it to.
Is this expected behavior or am I missing something?
variableState[count].answers...
Once counts becomes 1, variableState[1] is undefined and undefined.answers does not exists and you'll see an thrown error in your console.
I don't know if the variableStates value that you're showing in your code is the same as you're using on your end, but if you change it to variableState[0].answers..., it works.
I have a Ruler component and when I click on it, it creates horizontal ruler lines for me by adding 1 more to my redux store. So far I have no problems when I am adding them, but I also want to remove these rules when I double click on them, I send the item index through the action to my reducer, where I run an array.slice to remove just the rule that I double clicked on. But no matter what, it just pops the last rule for me.
I know that this is somehow happening when I am rendering the component, since when I console.log the array in the reducer, the correct element is removed from it, but it renders differently. Here are the codes I have used:
My Ruler component:
class Rulers extends Component {
render() {
const { mousePosition, wheelY, dragY, rulerHGuides } = this.props;
const ruleH = rulerHGuides.map((ruleH, i)=><RuleH index={i} wheelY={wheelY} dragY={dragY} key={i} {...ruleH} /> )
return (
<div>
<div className="rules">
{ ruleH }
</div>
<div className="ruler-horizontal" onClick={e=>{ store.dispatch( actions.rulerHGuides({top: mousePosition.y}) ) }}>
<span className="mouse-follow-h" style={{transform: `translateX(${mousePosition.x}px)`}}></span>
<svg id="svgH" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
</div>
)
}
}
export default connect(state=>{
const { mousePosition, rulerHGuides } = state;
return { mousePosition, rulerHGuides }
})(Rulers)
The RuleH component
class RuleH extends Component {
constructor(props) {
super(props)
this.state = {
rulezHDrag: 0,
top: 0
}
}
render() {
const { wheelY, dragY, index, id } = this.props;
const { rulezHDrag, top } = this.state;
return (
<DraggableCore onDrag={this.handleRuleH.bind(this)} enableUserSelectHack={false}>
<span id={id} className="ruleH" style={{top, transform: `translate3d(0, ${(-wheelY) + dragY + rulezHDrag}px, 0)`}}
onDoubleClick={e=>{
store.dispatch( actions.removeRulerHGuide( index ) )
console.log(index);
}}
></span>
</DraggableCore>
)
}
componentDidMount() {
this.setState({
top: `${this.props.top - ((-this.props.wheelY) + this.props.dragY)}px`
})
}
handleRuleH(e: MouseEvent, data: Object){
const { rulezHDrag } = this.state;
this.setState({
rulezHDrag: rulezHDrag + data.deltaY
})
}
}
My Action Creator:
// Ruler guides
// ==========================================
export const rulerHGuides = (hGuides) => {
return {
type: 'RULER_H_GUIDES',
hGuides
}
}
export const removeRulerHGuide = (index) => {
return {
type: 'REMOVE_RULER_H_GUIDE',
index
}
}
My Reducer:
export const rulerHGuides = (state = [], action) => {
switch (action.type) {
case 'RULER_H_GUIDES':
return [
...state,
action.hGuides
]
case 'REMOVE_RULER_H_GUIDE':
return [
...state.slice(0,action.index),
...state.slice(action.index + 1)
]
default:
return state;
}
}
I have tried using array.filters instead of array.slice, but that is not the problem, the issue, whatever it is, is happening inside my component where I am mapping my rulerHGuides array.
Instead of using indices please use some form of unique id and assign it to a rule at the creation time. When you use indices as keys for React components, the same index is used as a key after item is removed which breaks the re-rendering optimization.
For example, having indices 0, 1, 2, 3 and removing the item at 2 you are left with 0, 1, 2 - there you can see the last key is removed which is causing the issue you described.