My Parent class has two children
Counter component has state 'counter' which increments by the second;
class Counter extends Component {
constructor(props) {
super(props)
this.resetCount = this.resetCount.bind(this);
this.state = {
count : 0
}
}
resetCount() {
this.setState({
count : 0
});
}
componentDidMount() {
setInterval(() => {
this.setState({
count: this.state.count + 1
});
}, 1000);
}
render() {
const {count} = this.state;
const {color,size} = this.props;
return (
<Text style={{color, fontSize: size}}>{count}</Text>
);
}
}
In the Button Component, I have an onpress thing
<Button
onPress={resetCount}
title="Reset COunt"
color="#841584"
/>
In my main Parent Class I render
<Counter color={'green'} size={90} />
<Button/>
But I'm getting an error
'can't find variable resetCount' in App.js
You have to use 'this.resetCount' when using 'Button' inside Counter.render()
<Button
onPress={this.resetCount}
title="Reset COunt"
color="#841584"
/>
If Button is its own Component as mentioned you have to inherit the function onPress
Component Button
<Button onPress={this.props.onResetCount} ... />
Component Counter
render(){
return (
<Text style={{color, fontSize: size}}>{count}</Text>
<Button onResetCount={this.resetCount} title="Reset Count" color="... />
);
)
}
More detailed: https://reactjs.org/docs/faq-functions.html
This is due to Button not being able to access the class method inside its sibling Counter component. If your reorganise your code a little by moving the shared methods to the parent component you can a) achieve what you want, and b) make your code a little simpler. In other words make Counter the main component made up of two smaller dumb components / pure functions.
// No need for a component, just a function that returns
// a styled div
function Text({ count }) {
return <div>{count}</div>;
}
// Another function to return a button
function Button({ resetCount, text }) {
return <button onClick={resetCount}>{text}</button>;
}
// The main component that keeps the state which it passes
// to the dumb Text component
class Counter extends React.Component {
constructor() {
super()
this.state = { count: 0 };
this.resetCount = this.resetCount.bind(this);
}
componentDidMount() {
setInterval(() => {
this.setState({
count: this.state.count + 1
});
}, 1000);
}
resetCount() {
this.setState({ count: 0 });
}
render() {
return (
<div>
<Text count={this.state.count} />
<Button resetCount={this.resetCount} text="Reset count" />
</div>
)
}
}
ReactDOM.render(
<Counter />,
document.getElementById('container')
);
DEMO
You get the error because you can't do onPress={resetCount} this way. It is searching for the variable. But you don't have a variable, it's a function. So you should use this.resetCount if you want to access the function resetCount().
Here's an example how you can access the function of your parent component from the button in the child component:
// Parent component:
resetCount() {
// your code
}
render() {
return(
<Button resetCount={this.resetCount} /* your other stuff */ />
);
}
// Button component:
<button onPress={this.props.resetCount}>Click me</button>
Note: You can't update a sibling this way. You should move your functions from <Counter/> to your parent component.
Related
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 have a React <App> component with inside this structure:
{/*
INSIDE <APP>
<BreadCrumb>
<Chip/>
<Chip/>
<Chip/>
...
<BreadCrumb/>
<CardContainer>
<Card/> // just a clickable image
<Card/>
<Card/>
...
<Button/>
<CardContainer/>
*/}
I need a click on <Card> to activate a <Button> function, and this function should change the state of <App> as "activate" I mean that when I click on a <Card> the <Button> becomes clickable.
I have some problems to understand how pass function parents to children and set the state of a parent from inside a child.
this is my App component
class App extends React.Component {
constructor(props){
super(props)
this.state = {
activeIndex: 1
}
}
submitChoice() {
this.setState({activeIndex : this.state.activeIndex ++});
}
}
render() {
return (
<Button onClick = {this.submitChoice})/>
}
and this is the Button
class Button extends React.Component {
render() {
return(
<button
onClick = {() => this.props.onClick()}
className="button">
Continua
</button>
);
}
}
when i click on the button i receve this error
TypeError: Cannot read property 'activeIndex' of undefined
Use a property of the "Card" component to pass your callback function:
const Card = ({ onClick, id }) => {
const triggerClick = () => {
onClick(id);
};
return (
<div onClick={triggerClick}>Click the card</div>
);
};
const App = () => {
const cardClicked = id => {
console.log(`Card with id ${id} was clicked`);
//Modify App state here
};
return (
<CardContainer>
<Card onClick={cardClicked} id="card-1"/>
<Card onClick={cardClicked} id="card-2"/>
</CardContainer>
);
}
I'm currently following this and I did get it to work. But I would like to know if there is a way to stop the Query Render from reloading the data when calling this.setState(). Basically what I want is when I type into the textbox, I don't want to reload the data just yet but due to rendering issues, I need to set the state. I want the data to be reloaded ONLY when a button is clicked but the data will be based on the textbox value.
What I tried is separating the textbox value state from the actual variable passed to graphql, but it seems that regardless of variable change the Query will reload.
Here is the code FYR.
const query = graphql`
query TestComponentQuery($accountId: Int) {
viewer {
userWithAccount(accountId: $accountId) {
name
}
}
}
`;
class TestComponent extends React.Component{
constructor(props){
super(props);
this.state = {
accountId:14,
textboxValue: 14
}
}
onChange (event){
this.setState({textboxValue:event.target.value})
}
render () {
return (
<div>
<input type="text" onChange={this.onChange.bind(this)}/>
<QueryRenderer
environment={environment}
query={query}
variables={{
accountId: this.state.accountId,
}}
render={({ error, props }) => {
if (error) {
return (
<center>Error</center>
);
} else if (props) {
const { userWithAccount } = props.viewer;
console.log(userWithAccount)
return (
<ul>
{
userWithAccount.map(({name}) => (<li>{name}</li>))
}
</ul>
);
}
return (
<div>Loading</div>
);
}}
/>
</div>
);
}
}
Okay so my last answer didn't work as intended, so I thought I would create an entirely new example to demonstrate what I am talking about. Simply, the goal here is to have a child component within a parent component that only re-renders when it receives NEW props. Note, I have made use of the component lifecycle method shouldComponentUpdate() to prevent the Child component from re-rendering unless there is a change to the prop. Hope this helps with your problem.
class Child extends React.Component {
shouldComponentUpdate(nextProps) {
if (nextProps.id === this.props.id) {
return false
} else {
return true
}
}
componentDidUpdate() {
console.log("Child component updated")
}
render() {
return (
<div>
{`Current child ID prop: ${this.props.id}`}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
id: 14,
text: 15
}
}
onChange = (event) => {
this.setState({ text: event.target.value })
}
onClick = () => {
this.setState({ id: this.state.text })
}
render() {
return (
<div>
<input type='text' onChange={this.onChange} />
<button onClick={this.onClick}>Change ID</button>
<Child id={this.state.id} />
</div>
)
}
}
function App() {
return (
<div className="App">
<Parent />
</div>
);
}
I'm learning React and I'm trying to make a simple application: you click on a button and it increments a counter. I've prepared two components, ClickCounter and ClickButton, but I'm not sure how to connect them together. I've read different tutorials but they expect my components to be Parent/Child - is there something I'm completely missing from a architectural perspective?
class ClickCounter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return <h1>{this.state.count}</h1>;
}
}
function ClickButton(props) {
function handleClick(e) {
e.preventDefault();
console.log("clicked");
// increment the ClickCounter..how?
}
return (
<button id="btn" onClick={handleClick}>Click me</button>
);
}
function Container() {
return (
<div>
<ClickCounter />
<ClickButton />
</div>
);
}
const root = document.getElementById("root");
ReactDOM.render(<Container />, root);
A common technique for when two sibling components need to share some state is to lift the state up to the first common ancestor (Container in this case) and pass down the state and state-altering functions as props to the children.
Example
function ClickCounter(props) {
return <h1>{props.count}</h1>;
}
function ClickButton(props) {
return (
<button id="btn" onClick={props.handleClick}>Click me</button>
);
}
class Container extends React.Component {
state = { count: 0 };
onClick = () => {
this.setState(prevState => {
return { count: prevState.count + 1 };
});
};
render() {
return (
<div>
<ClickCounter count={this.state.count} />
<ClickButton handleClick={this.onClick} />
</div>
);
}
}
ReactDOM.render(<Container />, 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>
I'm learning React and am having trouble sending props to a click function. I'm trying to create a simple ES6 counter component that increments when a button is clicked.
My click function is simply:
click() {
this.setState({
count: this.state.count + value
})
}
I have set defaultProps as such:
Counter.defaultProps = { valueOne: 1 }
And have created my button, within the render function:
<Button className="btn" clickHandler={this.click} text="Click me" value={this.props.valueOne}/>
But I can't figure out who to get the button to 'send' the click function the value on click. I just get the message value is not defined.
Would anyone be able to point me in the right direction here?
Any help is appreciated.
My full code is:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.click = this.click.bind(this);
}
click() {
this.setState({
count: this.state.count + value
})
}
render() {
return (
<div className="container">
<h1>Count {this.state.count}</h1>
<Button className="btn blue-btn" clickHandler={this.click} text="Click me" value={this.props.valueOne}/>
</div>
)
}
}
Counter.defaultProps = { valueOne: 1 } //Defaults
const Button = (props) => {
return (
<button className={props.className} onClick={props.clickHandler} value={props.value}>{props.text}</button>
)
}
ReactDOM.render(
<Counter valueOne={1} valueTwo={10} valueThree={100} />,
document.getElementById('app')
);
It's in event.target.value, just change the handler to:
click(event) {
this.setState({
count: this.state.count + event.target.value
})
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0,
valueOne: 1
};
this.click = this.click.bind(this);
}
click(event) {
this.setState({
count: this.state.count + event.target.value
})
}
render() {
return (
<div className="container">
<h1>Count {this.state.count}</h1>
<Button className="btn blue-btn" clickHandler={this.click} text="Click me" value={this.state.valueOne}/>
</div>
)
}
}
Is this in essence what you are trying to do?
You can access the value with event.target.value.
But I notice there is a possible bug in your code, since this.setState could be executed asynchronously (as explained here). You better use the second version of setState that accept a callback, and not just an object. Like this:
click(event) {
this.setState((prevState) => {
return {
count: prevState.count + event.target.value
}
})
}