'React - interview exercise
I got the following React exercise with 2 components in an interview that I did not manage to make it compile... The question was the following:
Update the Counter component to take onIncrement callbacks as props and ensure they update the counter's values independently. Each callback should take a single, integer value as a parameter which is the amount to increment the counter's existing value by.
Comments in the code but the my problem is how to implement the "onIncrement" function.
const { Component } = React;
const { render } = ReactDOM;
// state data for 3 counters
const data = [
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 }
];
// Counter Component
class Counter extends Component {
render() {
const { value } = this.props;
return (
<div className="counter">
<b>{value}</b>
<div className="counter-controls">
<button className="button is-danger is-small">-</button>
//I call the function passed
<button className="button is-success is-small" onClick={()=>{onIncrement(this.props.value)}}>+</button>
</div>
</div>
);
}
}
class App extends Component {
constructor(props, context) {
super(props, context);
}
onIncrement=(value)=>{
//I tried several things here but I did not manage to make it work. I guess that I need also the id of the object...
}
render() {
return (
<div>
{data.map(counter => (
<Counter
key={counter.id}
value={counter.value}
//I pass the callback function to the component
onIncrement={this.onIncrement}
/>
))}
</div>
);
}
}
render(
<App/>
, document.querySelector('#root'))
Solution 1:[1]
Basically, you'll want to use the id as a way to determine which value you need to update. How you have it set up, you won't be able to know which value needs to be updated (because you don't know which id was clicked) nor will the value be saved.
NOTE: The example below takes the id from event.target.id and the value from event.target.value which is then deconstructed in the handleChange callback. This is a more common and elegant solution than passing a value to a callback and then passing it and another value to another callback (more work, more code, but same functionality).
Best solution: https://codesandbox.io/s/rjmx8vw99p
components/UpdateQuantity.js
import React, { Component, Fragment } from "react";
export default class App extends Component {
state = {
items: [
{ id: "Apples", quantity: 0 },
{ id: "Strawberries", quantity: 0 },
{ id: "Grapes", quantity: 0 },
{ id: "Apricots", quantity: 0 }
]
};
handleChange = ({ target: { id, value } }) => {
this.setState(prevState => ({
items: prevState.items.map(item => {
const nextVal = item.quantity + ~~value; // ~~ === parseInt(val, 10) -- required because the "value" is turned into a string when placed on a DOM element
return id === item.id
? { id, quantity: nextVal > 0 ? nextVal : 0 }
: { ...item };
})
}));
};
render = () => (
<div className="container">
<h1>Updating Values Inside Array</h1>
{this.state.items.map(({ id, quantity }) => (
<div key={id} className="container">
<div>
{id} ({quantity})
</div>
<button
id={id}
value={1}
style={{ marginRight: 10 }}
className="uk-button uk-button-primary"
onClick={this.handleChange}
>
+
</button>
<button
id={id}
value={-1}
style={{ marginRight: 10 }}
className="uk-button uk-button-danger"
onClick={this.handleChange}
>
-
</button>
</div>
))}
</div>
);
}
Another solution: https://codesandbox.io/s/yq961275rv (not recommended as it requires an extra component and an extra callback -- BUT there's no binding required in the render method nor is there an anonymous function () => {} in the onClick callback)
components/UpdateQuantity.js
import React, { Component, Fragment } from "react";
import Button from "./button";
export default class App extends Component {
state = {
items: [
{ id: "Apples", quantity: 0 },
{ id: "Strawberries", quantity: 0 },
{ id: "Grapes", quantity: 0 },
{ id: "Apricots", quantity: 0 }
]
};
handleChange = (id, val) => {
this.setState(prevState => ({
items: prevState.items.map(item => {
const nextVal = item.quantity + val;
return id === item.id
? { id, quantity: nextVal > 0 ? nextVal : 0 }
: { ...item };
})
}));
};
render = () => (
<div className="container">
<h1>Updating Values Inside Array</h1>
{this.state.items.map(props => (
<div key={props.id} className="container">
<div>
{props.id} ({props.quantity})
</div>
<Button
{...props}
className="uk-button uk-button-primary"
handleChange={this.handleChange}
value={1}
>
+
</Button>
<Button
{...props}
disabled={props.quantity === 0}
className="uk-button uk-button-danger"
handleChange={this.handleChange}
value={-1}
>
-
</Button>
</div>
))}
</div>
);
}
components/button.js
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
export default class Button extends PureComponent {
static propTypes = {
children: PropTypes.string.isRequired,
className: PropTypes.string,
disabled: PropTypes.bool,
id: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
value: PropTypes.number.isRequired
};
handleClick = () => {
this.props.handleChange(this.props.id, this.props.value);
};
render = () => (
<button
disabled={this.props.disabled || false}
className={this.props.className}
onClick={this.handleClick}
style={{ marginRight: 10 }}
>
{this.props.children}
</button>
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
