Reputation: 159
I have two components. MyButton and MyLabel component. I created 3 MyButton Compoents and 3 MyLabel Components. Each button has a different increment value. When you click on a button , the respective label should be updated not all the labels. At present all the labels are updating.
function MyButton(props) {
const onclick = () => {
props.onNumberIncrement(props.toBeIncremented);
};
return <button onClick={onclick}>+{props.toBeIncremented}</button>;
}
const MyLabel = function(props) {
return <label> {props.counter}</label>;
};
function App(props) {
const [counter, mySetCounter] = React.useState(0);
const handleClick = (incrementValue) => {
mySetCounter(counter + incrementValue);
};
return (
<div>
<MyButton
counter={counter}
onNumberIncrement={handleClick}
toBeIncremented={5}
/>
<MyButton
counter={counter}
onNumberIncrement={handleClick}
toBeIncremented={10}
/>
<MyButton
counter={counter}
onNumberIncrement={handleClick}
toBeIncremented={15}
/>
<br />
<MyLabel counter={counter} />
<MyLabel counter={counter} />
<MyLabel counter={counter} />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
jsfiddle: click here
Upvotes: 0
Views: 465
Reputation: 18113
Here is another solution using a single state variable :
function App(props) {
const [appState, setAppState] = React.useState([
{value: 0, incrementValue: 5},
{value: 0, incrementValue: 10},
{value: 0, incrementValue: 15}
]);
const handleClick = clickedIndex => () => {
setAppState(
appState.map((item, index) => clickedIndex !== index ?
item : ({...item, value: item.value + item.incrementValue})
)
);
};
return (
<div>
{
appState.map(({value, incrementValue}, index) => (
<MyButton
key={index}
counter={value}
onNumberIncrement={handleClick(index)}
toBeIncremented={incrementValue}
/>
));
}
<br />
{
appState.map(
({value}, index) => <MyLabel key={index} counter={value} />
);
}
</div>
);
}
Upvotes: 0
Reputation: 191976
Create a generator of button/label pairs with their local state and step. Generate the buttons and labels, and render them:
const useGenerateButtonAndLabel = step => {
const [counter, mySetCounter] = React.useState(0);
const onclick = React.useCallback(
() => mySetCounter(counter + step),
[step, counter]
);
return [
<button onClick={onclick}>+{step}</button>,
<label> {counter}</label>
];
};
function App(props) {
const [button1, label1] = useGenerateButtonAndLabel(5);
const [button2, label2] = useGenerateButtonAndLabel(10);
const [button3, label3] = useGenerateButtonAndLabel(15);
return (
<div>
{button1}
{button2}
{button3}
<br />
{label1}
{label2}
{label3}
</div>
);
}
ReactDOM.render(<App />, document.getElementById('demo'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="demo"></div>
If you also need a total, each generated pair can also return it's current counter
, and you can sum them in the parent. In this example, I also automate the items creation/rendering with Array.from()
, map, and reduce.
const useGenerateButtonAndLabel = step => {
const [counter, mySetCounter] = React.useState(0);
const onclick = React.useCallback(
() => mySetCounter(counter + step),
[step, counter]
);
// step is used here is a key, but if step is not unique, it will fail. You might want to generate a UUID here
return [
<button key={step} onClick={onclick}>+{step}</button>,
<label key={step}> {counter}</label>,
counter
];
};
const sum = items => items.reduce((r, [,, counter]) => r + counter, 0);
function App(props) {
const items = Array.from({ length: 5 },
(_, i) => useGenerateButtonAndLabel(5 * (i + 1))
);
return (
<div>
{items.map(([button]) => button)}
<br />
{items.map(([, label]) => label)}
<div>Total: {sum(items)}</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('demo'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Upvotes: 1