i am tryign to write method for voting everytime user click button it should increment by 1 it is happening with below code
retro.js
export class RetroComponent extends Component {
constructor(props) {
super(props);
this.textareaRef = React.createRef();
this.state = {
value: 0
}
}
addCard(){
console.log("add card");
}
incrementWentWell() {
// eslint-disable-next-line react/no-direct-mutation-state
return ++this.state.value;
}
render() {
return (
<IconButton onClick={() => this.incrementWentWell()}>
<ThumbUpTwoToneIcon />
</IconButton>
<h5 style={{marginRight: 10}}><p>{this.state.value}</p></h5>
)}
}
That still counts as a state mutation (huge anti-pattern!). Ignoring the warning doesn't make the behavior change. Use a functional state update to take the existing state value, add 1 to it, and return a new state object so react can reconcile the change and update the UI/DOM.
incrementWentWell() {
this.setState(state => ({ value: state.value + 1 })
}
Related
Hello Guys I am a newbie and trying to learn React. I want to access the state of counters.js from counter.js in the delete button. I tried to print but the output is giving as undefined. The expected output is the button id clicked. Please somebody help. and can someone suggest me a good course to learn React and front-end development?
counter.js
class Counter extends Component {
state = {
value: this.props.value
}
handleIncrement = () => {
this.setState({
value: this.state.value + 1
})
}
render() {
return (<div>
{this.props.children}
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button onClick={this.handleIncrement} className='btn btn-secondary btn-sm'>Increment</button>
<button onClick={() => this.props.onDelete(this.props.id)} className="btn btn-danger btn-sm m-2">Delete</button>
</div>);
}
formatCount() {
const {value} = this.state;
return value === 0
? "Zero"
: value;
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.value === 0
? "warning"
: "primary";
return classes;
}
}
export default Counter;
Counters.js
import React, {Component} from 'react';
import Counter from './counter';
class Counters extends Component {
state = {
counters: [
{
id: 1,
value: 4
}, {
id: 2,
value: 0
}, {
id: 3,
value: 0
}, {
id: 4,
value: 0
}
]
};
handleDelete = (counterId) => {
console.log('Event Handler Called', counterId);
}
render() {
return (<div>
{this.state.counters.map(counter => (<Counter key={counter.id} onDelete={this.handleDelete} value={counter.value}/>))}
</div>)
}
}
export default Counters;
console
,
change word key to any other letter) and it should works fine
{this.state.counters.map(counter => (<Counter key={counter.id}
to
{this.state.counters.map(counter => (<Counter a={counter.id}
and this line also
<button onClick={() => this.props.onDelete(this.props.key)}
to
<button onClick={() => this.props.onDelete(this.props.a)}
also you can add a key in this way
(counter, i) => (<Counter key={i} a={counter.id}
here is your working solution in two files
Counters.js
export default class Counters extends Component {
constructor(props) {
super(props)
this.state = {
counters: [
{
id: 1,
value: 4
}, {
id: 2,
value: 0
}, {
id: 3,
value: 0
}, {
id: 4,
value: 0
}
]
};
this.handleDelete = this.handleDelete.bind(this)
}
handleDelete = (counterId) => {
console.log(counterId)
console.log('Event Handler Called', counterId);
}
render() {
return (<div>
{this.state.counters.map(counter => (<Counter a={counter.id} onDelete={this.handleDelete} value={counter.value}/>))}
</div>)
}
}
counter.js
import React from 'react'
class Counter extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.getBadgeClasses = this.getBadgeClasses.bind(this)
this.formatCount = this.formatCount.bind(this)
}
handleIncrement = () => {
this.setState({
value: this.state.value + 1
})
}
render() {
return (<div>
{this.props.children}
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button onClick={this.handleIncrement} className='btn btn-secondary btn-sm'>Increment</button>
<button onClick={() => this.props.onDelete(this.props.a)} className="btn btn-danger btn-sm m-2">Delete</button>
</div>);
}
formatCount() {
const {value} = this.state;
return value === 0
? "Zero"
: value;
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.value === 0
? "warning"
: "primary";
return classes;
}
}
export default Counter;
Please refer the following link :
Passing function to components
and don't forget to bind your function to component instance.
Answer to second question:
If you have github student account (for free access), checkout Frontend Masters. It is an excellent resource for frontend development.
You cannot access a component’s state from outside the component. As it’s stated in the official React documentation:
State is similar to props, but it is private and fully controlled by
the component.
Basically there are two approaches for “exchanging” the state between components:
From parent components to child components with props: Just as you did with the value property of your Counter component
From child components to parent components with event handlers:
If certain actions should trigger a state change of a parent’s component you need to inform the parent about the action with an event handler. You already did that with your onDelete handler.
These are the only two mechanisms in React to "exchange" the state. In your case I suggest to remove the state of your Counter component and to use the value property instead. Additionally you need to add an onIncrement handler (just as you did with onDelete) and move the handleIncrement function to your Counters component. Observe that you need to pass the counter’s id to the handleIncrement function now (since the Counters component does not know which child component triggered the event).
If you apply these changes, the Counter component is stateless and the state of all counters is controlled by the Counters component. Nevertheless since you pass the state via the value property it still gets updated each time the state changed.
For diving into react I suggest to do the React tutorial for beginners (https://reactjs.org/tutorial/tutorial.html) – your issue is addressed here throughout the tutorial (look for “lifting up the state”) and afterwards you could continue with the official documentation (https://reactjs.org/docs/hello-world.html).
You are passing only key, onDelete and value props to component.
{this.state.counters.map(counter => (<Counter key={counter.id} onDelete={this.handleDelete} value={counter.value}/>))}
And you are trying to access this.props.id on delete button click inside counter.js file.
<button onClick={() => this.props.onDelete(this.props.id)} className="btn btn-danger btn-sm m-2">Delete</button>
you should use this.props.key on delete button click instead of this.props.id. The correct delete button code will be like
<button onClick={() => this.props.onDelete(this.props.key)} className="btn btn-danger btn-sm m-2">Delete</button>
You can go through React official documents for better learning of React.
The reason you are getting undefined is because you are not passing id prop for Counter component
{this.state.counters.map(counter => (<Counter key={counter.id} onDelete={this.handleDelete} value={counter.value}/>))}
So passing id prop will work as your expectation:
{this.state.counters.map(counter => (<Counter id={counter.id} key={counter.id} onDelete={this.handleDelete} value={counter.value}/>))}
This is one of the reasons we need some type check tools like PropTypes or Typescript so this kind of mistake can be avoided at compile time.
The best resource for learning React is actually React official documentation
https://reactjs.org/docs/getting-started.html
Once you can digest this already then next step can subscribe Medium to get a lot of good reads so you can have deeper understandings from many aspects and use cases.
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
I want to dynamically add Components, after clicking the "add" button.
For that, I created an array that consists of all the components, and add them on click.
My problem is, that it only renders one component, even though it consists of several ones.
My code looks like this:
class QuestionBlock extends React.Component {
constructor(props) {
super(props);
this.state = {answersArray: []};
}
addPossibleAnswer() {
this.state.answersArray.push(
<PossibleAnswers id={this.state.answersArray.length + 1}/>
)
this.forceUpdate();
}
componentWillMount() {
this.state.answersArray.push(
<PossibleAnswers id={this.state.answersArray.length + 1}/>
)
}
render() {
console.log(this.state.answersArray) // Grows after adding componenets, but they are not rendered.
return (
<div>
{this.state.answersArray}
<AddPossibleAnswer addPossibleAnswer={() => this.addPossibleAnswer()} />
</div>
);
}
}
If you see what I did wrong, I'd be really glad if you could help me out!
Instead of mutating state directly and adding JSX to it, you can instead keep raw data in your state and derive the JSX from that in the render method instead.
Example
class QuestionBlock extends React.Component {
state = { answers: 1 };
addPossibleAnswer = () => {
this.setState(({ answers }) => ({ answers: answers + 1 }));
};
render() {
return (
<div>
{Array.from({ length: this.state.answers }, (_, index) => (
<PossibleAnswers key={index} id={index} />
))}
<AddPossibleAnswer addPossibleAnswer={this.addPossibleAnswer} />
</div>
);
}
}
You don't interact with state like you do. Never mutate the state field. You need to use this.setState:
this.setState(prevState => ({answersArray: prevState.answersArray.concat([
<PossibleAnswers id={prevState.answersArray.length + 1}])}));
Having said that, it is also strange that you store components in state. Usually, you would store data and create the components based on the data in the render method.
You are directly pushing elements to the array without setState so the component won't re-render
Also avoid using tthis.forceUpdate() as much as you can in your application because this is not recommended much
You need to change your code like below. The recommended approach for dealing with arrays in react is using previous state and push to an array
addPossibleAnswer() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
componentWillMount() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
Also keep in mind that componentWillMount life cycle method is deprecated in react 16. So move the code to componentDidMount instead
Here is the corrected code
class QuestionBlock extends React.Component {
constructor(props) {
super(props);
this.state = {answersArray: []};
}
addPossibleAnswer() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
componentDidMount() {
this.setState(prevState => ({
answersArray: [...prevState.answersArray, <PossibleAnswers id={prevState.answersArray.length + 1}/>]
}));
}
render() {
const { answersArray } = this.state;
return (
<div>
{answersArray}
<AddPossibleAnswer addPossibleAnswer={() => this.addPossibleAnswer()} />
</div>
);
}
}
reproduced here: https://jsfiddle.net/69z2wepo/204131/
A parent component has two 'notifications' that it renders with different 'decay' rates.
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
notifications: [
{ message: "I am the first component", code: 1, decay: 2000 },
{ message: "I am the second component", code: 2, decay: 5000 }
]
}
this.dismissNotification = this.dismissNotification.bind(this)
}
dismissNotification(code) {
this.setState({ notifications: this.state.notifications.filter(
n => n.code != code
)})
}
render() {
return (
<div>
{
this.state.notifications.map( (n, idx) => {
return (
<Notification
key={idx}
code={n.code}
decay={n.decay}
dismiss={this.dismissNotification}
>
{n.message}
</Notification>
)
})
}
</div>
)
}
}
The components set their own timeOut which will cause an animation and then send a message for them to be dismissed.
class Notification extends React.Component {
constructor(props) {
super(props)
this.state = {
style: { opacity: 1 }
}
this.makeRedFunction = this.makeRedFunction.bind(this)
}
componentDidMount = () => {
let timeout = parseInt(this.props.decay) || 2000
setTimeout(() => {
this.makeRedFunction();
setTimeout(() => {
this.dismiss();
}, 125)
}, timeout)
}
fadeOutFunction = () => {
let opacity = Math.floor(this.state.style.opacity * 10)
if (opacity > 0) {
opacity -= 1
setTimeout( () => { this.fadeOutFunction() }, 10)
}
let newState = Object.assign({}, this.state.style)
newState.opacity = opacity / 10
this.setState({ style: newState })
}
makeRedFunction = () => {
this.setState({ style: {color: 'red'} })
}
dismiss = () => {
this.props.dismiss(this.props.code)
}
render () {
return(
<div style={this.state.style}>{this.props.children}</div>
)
}
}
ReactDOM.render(
<Page/>,
document.getElementById('container')
);
Unforunately, the style seems to change for both notifications when the dismiss function has been called for only one of them.
In general there is strange behavior with the mounting lifecycle of the components with this approach.
tl;dr: Don't use array indexes as keys if elements in the list have state. Use something that is unique for each data point and does not depend on its position in the array. In your case that would be key={n.code}.
This is related to how React reconciles the component tree and is a good example for why using array index as keys doesn't always produce the expected outcome.
When you are mutating a list of elements, the key helps React to figure out which nodes it should reuse. In your case are going from
<Notification />
<Notification />
to
<Notification />
But how should React know whether to delete the first or second <Notification /> node? It does that by using keys. Assume we have
<Notification key="a">Foo</Notification>
<Notification key="b">Bar</Notification>
Now if it gets either
<Notification key="a">...</Notification>
or
<Notification key="b">...</Notification>
in the next render cycle it knows to remove the <Notification /> with key "b" (or "a").
However, your problem is that you base the key on the position of the data in the array. So on the first render you pass
<Notification key="0">First</Notification>
<Notification key="1">Second</Notification>
Then you are removing the first notification from the list, which changes the position of the second notification in the array, so React gets
<Notification key="0">Second</Notification>
which means
remove the element with key 1 and update the element with key 0 to show "Second"
But the element with key="0" already had its style changed to red text, so you see the text from the second notification in red.
Have a look at the documentation for more information.
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.