Я получил следующее упражнение React с двумя компонентами в интервью, которое мне не удалось скомпилировать... Вопрос был следующий:
Обновите компонент Counter, чтобы он принимал обратные вызовы onIncrement в качестве реквизита и обеспечивал независимое обновление значений счетчика. Каждый обратный вызов должен принимать одно целочисленное значение в качестве параметра, который является суммой, на которую увеличивается существующее значение счетчика.
Комментарии в коде, но моя проблема заключается в том, как реализовать функцию «onIncrement».
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'))Итак, в чем ваш вопрос/проблема?
Отредактировано. Как реализовать функцию onIncrement, извините.
Вот подсказка: ваш компонент Counter не ожидает/не использует поддержку onIncrement. Вы должны использовать const { value, onIncrement } = this.props; в методе рендеринга.
Правда, я пропустил, я просто забыл об этом, спасибо :), но моя проблема связана с реализацией OnIncrement, там я не в курсе, еще одна подсказка? :)
Честно говоря, не передав никакой информации о том, какой счетчик будет обновляться, как вы могли его обновить? Я имею в виду, что когда вы вызываете onIncrement() из Counter Компонента, если вы каким-то образом не передаете информацию, для которой Counter вы увеличиваете, вы не можете ожидать, что узнаете «по волшебству» от App Компонента. Кстати, const data на самом деле находится в состоянии App Component? Потому что вы не создаете состояние в компоненте App



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


По сути, вы захотите использовать id как способ определить, какие value вам нужно обновить. Как вы это настроили, вы не сможете узнать, какой value нужно обновить (потому что вы не знаете, какой id был нажат), и value не будет сохранен.
ПРИМЕЧАНИЕ: в приведенном ниже примере берется id из event.target.id и value из event.target.value, которые затем деконструируются в обратном вызове handleChangeEvent. Это более распространенное и элегантное решение, чем передача значения в callback, а затем передача его и другого значения другому callback (больше работы, больше кода, но та же функциональность).
Лучшее решение: https://codesandbox.io/s/rjmx8vw99p
компоненты/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 }
]
};
handleChangeEvent = ({ 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.handleChangeEvent}
>
+
</button>
<button
id = {id}
value = {-1}
style = {{ marginRight: 10 }}
className = "uk-button uk-button-danger"
onClick = {this.handleChangeEvent}
>
-
</button>
</div>
))}
</div>
);
}
Другое решение: https://codesandbox.io/s/yq961275rv (не рекомендуется, так как для этого требуется дополнительный компонент и дополнительный обратный вызов, НО в методе render не требуется ни привязки, ни анонимной функции () => {} в обратном вызове onClick)
компоненты/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 }
]
};
handleChangeEvent = (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"
handleChangeEvent = {this.handleChangeEvent}
value = {1}
>
+
</Button>
<Button
{...props}
disabled = {props.quantity === 0}
className = "uk-button uk-button-danger"
handleChangeEvent = {this.handleChangeEvent}
value = {-1}
>
-
</Button>
</div>
))}
</div>
);
}
компоненты /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,
handleChangeEvent: PropTypes.func.isRequired,
value: PropTypes.number.isRequired
};
handleClick = () => {
this.props.handleChangeEvent(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>
);
}
Обновленный ответ и объяснение. Кроме того, я включил альтернативное решение для принятого ответа, которое позволяет избежать привязки в методе рендеринга.
Я знаю, что ответ был принят, но на самом деле он не полностью удовлетворяет требованиям, т.е.
Each callback should take a single, integer value as a parameter which is the amount to increment the counter's existing value by.
Принятый ответ принимает объект события в качестве параметра, который не является указанным требованием. Единственный способ строго удовлетворить ожидаемое требование — привязать уникальный
"...onIncrement callbacks as props..."
для каждого счетчика. Этот подход имеет недостатки и влияет на производительность как описано в этой статье
Рабочий пример наhttps://codesandbox.io/s/j2vxj620z9
Да, согласен с тобой. Я пробовал решение сегодня и думал о том же! :)
Где вы столкнулись с проблемой или отсутствием знаний о том, как действовать? Вы не указали конкретную проблему