I'm practice send state to Child-component - Parent-Component - Another-Child-Component.
and I was stuck on my practice,
First, I was successful to make react counter like this
child component
Second, I want to send counter value to another component.
I know how to send state to another child component.
But i stuck my work.
Send state to another component
when i first clicked + button, it works only First child.
and then, when i clicked once more, it works another child too(not match number)
How can I dealing this problem?
This is my code.
// This is Counter.js
class Counter extends Component {
state = {
counter: 0
}
handleIncrement = () => {
this.setState(({ counter }) => ({
counter: counter + 1
}))
this.props.handleCounter(this.state.counter)
}
handleDecrement = () => {
this.setState(({counter}) => ({
counter: counter - 1
}))
this.props.handleCounter(this.state.counter)
}
render() {
return (
<div>
<h1>Counter</h1>
<h3>{this.state.counter}</h3>
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
)
}
}
// This is App.js file
import Counter from './components/counter';
import Sent from './components/sent'
class App extends React.Component {
state = {
counter: this.handleCounter
}
handleCounter = (counter) => {
console.log("Received Count 1 ")
this.setState({
counter: counter
})
}
render() {
return (
<div className="App">
<Counter handleCounter={this.handleCounter} />
<Sent result={this.state.counter} />
</div>
);
}
}
// This is Sent.js file
import React, { Component } from 'react'
class Sent extends React.Component {
render() {
return (
<div>
<h2>Result ==> {this.props.result}</h2>
</div>
)
}
}
Related
I'm confused about the refactoring of my code.
My Component works like this ==> I set the incoming props from the parent component to the child component's state. Then I can do the update state operations in the component.
This is my UNSAFE_componentWillReceiveProps function:
UNSAFE_componentWillReceiveProps = (nextProps) => {
if (nextProps
&& nextProps.actionToEdit
&& nextProps.actionToEdit.statement
) {
const regex = /#\w+/g;
const found = nextProps.actionToEdit.statement.match(regex);
this.setState({
parameters: found
});
}
};
This is my getDerivedStateFromProps function:
static getDerivedStateFromProps(
nextProps: ICustomActionModalProps,
prevState: ICustomActionModalState
) {
if (nextProps
&& nextProps.actionToEdit
&& nextProps.actionToEdit.statement
) {
const regex = /#\w+/g;
const found = nextProps.actionToEdit.statement.match(regex);
return {
parameters: found
};
}
return null;
}
This is my onChange function:
onChange = (newValue, e) => {
const regex = /#\w+/g;
const found = newValue.match(regex);
this.setState({ parameters: found });
};
I realized something when onChange worked.
==> If I use UNSAFE_componentWillReceiveProps, the state updated perfectly. Because when the onChange function works every time UNSAFE_componentWillReceiveProps doesn't work.
However,
==> If I use getDerivedStateFromProps, the state updated with old props. Because when the onChange function works every time getDerivedStateFromProps works too.
I just want my getDerivedStateFromProps function will able to works like my old UNSAFE_componentWillReceiveProps function.
How can I do that?
Thanks
The problem you're describing is solved either by making your Component fully controlled or fully uncontrolled but with a key.
The first approach may look like this:
// --- parent component: Parent.tsx
import { Child } from "./Child";
export class Parent extends React.Component<{}, { counter: number }> {
state = { counter: 0 };
render() {
return (
<div>
<div>
<button onClick={() => this.setState(({ counter }) => ({ counter: counter + 1 }))}>increase counter</button>
</div>
<Child
parentCounter={this.state.counter}
setParentCounter={(n) => this.setState({ counter: n })}
/>
</div>
);
}
}
// --- child component : Child.tsx
type Props = { parentCounter: number; setParentCounter: (n: number) => void };
export class Child extends React.Component<Props> {
render() {
const { parentCounter, setParentCounter } = this.props;
return (
<>
<div>parent counter: {parentCounter}</div>
<button
onClick={() => {
setParentCounter(parentCounter + 1);
}}
>
increase parent counter
</button>
</>
);
}
}
So, there is not two separate states: one in the parent component and another in the child one. The only state exists in the parent component and child component has the setter prop it may use to change parent's state.
The second approach (uncontrolled component with a key) uses the fact that when the key changes the child component rerenders from scratch and looses it's inner state:
// --- Parent.tsx
import { Child } from "./Child";
export class Parent extends React.Component<{}, { counter: number }> {
state = { counter: 0 };
render() {
const { counter } = this.state;
return (
<div>
<div>parent counter: {counter}</div>
<div>
<button
onClick={() =>
this.setState(({ counter }) => ({ counter: counter + 1 }))
}
>
increase parent counter
</button>
</div>
<Child parentCounter={counter} key={`child-key-${counter}`} />
</div>
);
}
}
// --- Child.tsx
type Props = { parentCounter: number };
type State = { childCounter: number };
export class Child extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { childCounter: props.parentCounter };
}
render() {
return (
<>
<div>child counter {this.state.childCounter}</div>
<button
onClick={() => {
this.setState(({ childCounter }) => ({
childCounter: childCounter + 1
}));
}}
>
increase child counter
</button>
</>
);
}
}
I currently have a parent class (App) and a child component (FoodItem). The App component maps a list of FoodItem components.
Each individual FoodItem component has a state called clickCount which increments each time its clicked by the user. However, I need the reset button in my App component to reset the FoodItem state to 0 onClick for all FoodItem components in the map.
Any help with this would be much appreciated.
Update - I have managed to get the reset button to update the FoodItem child component, however it only updates the last item in the mapped list, whereas I require it to update all items in the list.
class App extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.state = {
foods: FoodCards,
calorieCount: 0,
vitaminACount: 0,
vitaminDCount: 0,
};
this.increment = this.increment.bind(this);
}
increment = (calories = 0, vitaminA = 0, vitaminD = 0) => {
this.setState({
calorieCount: Math.round(this.state.calorieCount + calories),
vitaminACount: Math.round((this.state.vitaminACount + vitaminA) * 100) / 100,
vitaminDCount: Math.round((this.state.vitaminDCount + vitaminD) * 100) / 100
});
};
resetCounters = () => {
this.myRef.current.resetClickCount();
this.setState({
calorieCount: 0,
vitaminACount: 0,
vitaminDCount: 0,
});
};
render() {
return (
<div className="App">
<main className="products-grid flex flex-wrap">
{this.state.foods.map((item, i) => {
return <FoodItem key={item.id} ref={this.myRef} name={item.name} img={item.img} calories={item.calories} vitamin_a={item.vitamin_a} vitamin_d={item.vitamin_d} updateTotals = {this.increment} />
})}
</main>
<footer>
<div className="reset" onClick={() => this.resetCounters()}>Reset</div>
</footer>
</div>
);
}
}
export default App;
class FoodItem extends React.Component {
constructor(props) {
super(props);
this.state = {
clickCount: 0
};
}
handleUpdateTotals = (calories, vitamin_a, vitamin_d) => {
this.props.updateTotals(calories, vitamin_a, vitamin_d);
this.clickIncrement();
}
clickIncrement = () => {
this.setState({
clickCount: this.state.clickCount + 1
});
}
resetClickCount = () => {
this.setState({
clickCount: 0
});
}
render() {
return (
<div className="product" onClick={() => this.handleUpdateTotals(this.props.calories, this.props.vitamin_a, this.props.vitamin_d)}>
<p>{this.props.name}</p>
{this.state.clickCount > 0 ? <p>Selected: {this.state.clickCount}</p> : <p>Not Selected</p>}
<img src={this.props.img} alt="" />
</div>
);
}
}
You should lift the state up per the react docs. When two children affect the same state (reset button and clickCount), the state should be lifted up to the parent that contains both of these children. So your count should be stored in the parent.
But... to answer your question, you can easily do something like this to trigger a render each time the button is clicked (not recommended though):
const [resetCount, setResetCount] = useState(false);
const resetCountClicked = () => setResetCount(!resetCount);
return
<>
<Child resetCount={resetCount}></Child>
<button onClick={() => resetCountClicked()}>Reset Count</button>
</>
And in the child include:
useEffect(() => setCount(0), [resetCount]);
This is using hooks with function components and useEffect to run an event each time the prop resetCount changes.
With classes you can use a ref passed to the child as shown in this post.
const { Component } = React;
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.resetCount();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Reset Count</button>
</div>
);
}
}
And in the child...
resetCount() {
this.setState({
clickCount: 0
});
}
I have a working countdown clock except for the fact that it begins counting down one 'turn' after it should. I have a button which starts the game and starts the clock. I press it and it retrieves and shows my random number (say 5) and displays my random number of inputs (5). But the countdown says 0. which is (20 * null) of the loading page (this.state loads as null). I press start again, and there is a new random number but the countdown begins counting down from (5 * 20) instead of the new random number. I am a bit at a loss. Any help would be greatly appreciated.
The whole code
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
let timer = null
class StartButton extends React.Component {
render(props) {
return (
<button onClick={this.props.onClick}>{this.props.name}</button>
)
}
}
class Header extends React.Component {
constructor() {
super();
this.state = {
ranNum: null,
restart: false,
timer: null
};
}
handleClick() {
clearTimeout(timer);
this.clearForm();
this.generateRanNum();
this.generateInputs();
// this.changeToRestartText()
this.countdownClock();
}
generateRanNum = () => {
let ranNum = Math.floor(Math.random() * 20);
this.setState({
ranNum: ranNum,
})
}
clearForm = () => {
if(this.state.restart === true){
const inputLi = <Input />;
inputLi.map(element => {
element.remove()
});
const subButton = <SubmitButton />
subButton.remove()
}
}
countdownClock = async () => {
const startingNum = this.state.ranNum * 20;
for(let i = startingNum; i >= 0; i--) {
await new Promise(resolve => {
timer = setTimeout(() => {
this.setState({
timer: i
})
resolve()
}, 1000)
});
}
}
generateInputs = () => {
const inputs = []
for(let i = 1; i <= this.state.ranNum; i++){
inputs.push(
<Input type='text' className='textInputs' />
)
}
return inputs;
}
generateSubButton = () => {
return (
<SubmitButton name='Submit Button' />
)
}
render() {
return (
<div>
<header>
<div className="headerContainer">
<div id="countdown">
<Countdown name='Countdown: ' countdown={this.state.timer} />
</div>
<div className="flex-item-header">
<StartButton
name={!this.state.restart ? 'Start Button' : 'Restart Button'}
onClick={() => this.handleClick()}
/>
</div>
<div>
<DisplayCount name='Word Count: ' count={this.state.ranNum} />
</div>
</div>
</header>
<section>
<div className="flex-main-item">
<ul>
{this.generateInputs()}
</ul>
{this.generateSubButton()}
</div>
</section>
</div>
)
}
}
class SubmitButton extends React.Component {
render(props) {
return (
<button name={this.props.name} onClick={this.props.onClick}>
</button>
)
}
}
class DisplayCount extends React.Component {
render() {
return (
<p>
{this.props.name}
<span>{this.props.count}</span>
</p>
)
}
}
class Countdown extends React.Component {
render(props) {
return (
<p>
{this.props.name}
<span>{this.props.countdown}</span>
</p>
)
}
}
You didn't post your code for generateRanNum but unless it's setting the state, then the first time you run this.countdownClock(), state.ranNum is still null from the initalization of the component. You need to set that state first before you start counting down.
Moral of story: Setting component state in React is async.
You set ranNum state in generateRanNum() then you read this.state.ranNum in countdownClock(), expecting it to be up-to-date. Well, it’s not because setState() is async.
I guess you’re aware of the concept of component lifecycle. Any call to setState() will cause a round of call to lifecycle hook methods. But these calls are buffered and batch applied for performance reason, thus async.
this.state is not updated until componentWillUpdate() of "next round" is called. Yet you access it prematurely in "this round". Thus the countdown lags behind one turn, cus it indeed reads the old value.
Quick fix to your code:
const ranNum = this.generateRanNun();
this.countdownClock(ranNum);
Cheers.
I have an auction section in my React app, but can't quite get it to work. I would like for the page to render the added auction amount to the original with the starting bid. I've tried doing this without state and have gone through various iterations and haven't gotten to work.
Below you'll find the code of my most recent code iteration from my Auction component:
import React from "react"
import AuctionItems from "../AuctionItems"
class Auction extends React.Component {
constructor() {
super()
this.state = {
bid: 0
}
this.addHundred = this.addHundred.bind(this)
}
addHundred() {
this.setState((state) => {
console.log ({bid: state.bid + 100})
})
}
render() {
const items = AuctionItems.map(item => {
return (
<div>
<h4>{item.name}</h4>
<h4>${item.starting_bid}</h4>
<button onClick={this.addHundred}>Bid $100</button>
</div>
)
})
return (
<div>
<h1>Auction</h1>
{items}
</div>
)
}
}
export default Auction
Your state needs to be a bit more complex because you want to track bids for each item. The following uses a bids piece of your state that stores each item's bid keyed by the item name.
import React from "react"
import AuctionItems from "../AuctionItems"
class Auction extends React.Component {
constructor() {
super()
this.state = {
bids: {}
}
this.addHundred = this.addHundred.bind(this)
}
addHundred(item) {
this.setState((state) => ({
bids: {
...state.bids,
[item.name]: (state.bids[item.name] || 0) + 100
}
}));
}
render() {
const items = AuctionItems.map(item => {
return (
<div>
<h4>{item.name}</h4>
<h4>
${item.starting_bid + (this.state.bids[item.name] || 0)}
</h4>
<button onClick={() => this.addHundred(item)}>
Bid $100
</button>
</div>
)
})
return (
<div>
<h1>Auction</h1>
{items}
</div>
)
}
}
export default Auction
Please forgive me, I am new to programming and JavaScript/React...
This is the question from my assignment:
Make a counter application using React and Node.js. The user must have the ability to click a button to increase, decrease, or reset a counter. The app must have the following components: Display, DecreaseCount , IncreaseCount, ResetCount. Pass the appropriate functions to be used and current counter value to each component.
I'm not sure what the point is of creating components for those simple operations. I also don't understand what will make those arithmetical components unique if I'm passing them both a function and a value to work on. But I am assuming the point of the assignment is to show that you can pass state to a child, work on it within the child, and then pass the worked-on result back to the parent to be stored in its state.
Here is the main page, Display.js.
For now I'm just trying to get the add functionality to work:
import React, { Component } from 'react';
import IncreaseCount from './IncreaseCount';
import DecreaseCount from './DecreaseCount';
import ResetCount from './ResetCount';
class Display extends Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.state = {
count: 0
};
}
increment = numToInc => {
this.setState({ count: numToInc++ });
};
decrement = numToDec => {
this.setState({ count: numToDec-- });
};
reset = numToReset => {
numToReset = 0;
this.setState({ count: numToReset });
};
render() {
return (
<div>
<h2>{this.state.count} </h2>
<IncreaseCount count={this.state.count} operation={this.increment} />
<DecreaseCount count={this.state.count} operation={this.decrement} />
<IncreaseCount count={this.state.count} operation={this.reset} />
</div>
);
}
}
export default Display;
And here is the IncreaseCount component class:
import React, { Component } from 'react';
class IncreaseCount extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
buttonClick = () => {
this.setState({ count: this.props.count }); // I am pretty sure this isn't necessary
this.props.operation(this.state.count);
};
render() {
return <button onClick={this.buttonClick}></button>;
}
}
export default IncreaseCount;
It is not throwing any errors but is not changing the value of either the Increase count or the Display count properties. I was expecting both to be changing in lockstep. My goal is to send the incremented value back to Display to be displayed. Is there a problem with the way I've written and passed my increment function?
You need to use this.props.count within the IncreaseCount
class IncreaseCount extends Component {
buttonClick = () => {
this.props.operation(this.props.count);
};
...
}
A full example might look something like this:
class Display extends Component {
state = {
count: 0
};
increment = numToInc => {
this.setState({ count: numToInc + 1 });
};
decrement = numToDec => {
this.setState({ count: numToDec - 1 });
};
reset = () => {
this.setState({ count: 0 });
};
render() {
return (
<div>
<h2>{this.state.count} </h2>
<Operation
name="+"
count={this.state.count}
operation={this.increment}
/>
<Operation
name="-"
count={this.state.count}
operation={this.decrement}
/>
<Operation
name="Reset"
count={this.state.count}
operation={this.reset}
/>
</div>
);
}
}
class Operation extends Component {
buttonClick = () => {
this.props.operation(this.props.count);
};
render() {
return <button onClick={this.buttonClick}>{this.props.name}</button>;
}
}
Note that you don't have to pass the counter value to each Operation and use a functional setState:
increment = () => {
this.setState(prev => ({ count: prev.count + 1 }));
};
Using a single component like <Operation /> is certainly how I'd do it. However, per the requirements of the OP, I'm adding this example that uses all 4 components specified.
import React, { Component } from 'react';
class IncreaseCount extends Component {
render(props) {
return <button onClick={this.props.action}>+</button>;
}
}
class DecreaseCount extends Component {
render(props) {
return <button onClick={this.props.action}>-</button>;
}
}
class ResetCount extends Component {
render(props) {
return <button onClick={this.props.action}>reset</button>;
}
}
class Display extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.reset = this.reset.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
if (this.state.count > 0) {
this.setState({ count: this.state.count - 1 });
}
}
reset() {
this.setState({ count: 0 });
}
render() {
return (
<div>
<h2>{this.state.count}</h2>
<DecreaseCount count={this.state.count} action={this.decrement} />
<IncreaseCount count={this.state.count} action={this.increment} />
<ResetCount count={this.state.count} action={this.reset} />
</div>
);
}
}
export default Display;
This version also prevents the counter from going below 0.