Есть несколько проблем с вашим кодом, поэтому я буду go просматривать их по одному:
Не хранить JSX в состоянии
Состояние для хранение сериализуемых данных, а не пользовательский интерфейс. Вы можете хранить числа, логические значения, строки, массивы, объекты и т. Д. c ... но не хранить компоненты.
Сохраняйте свой JSX простым
JSX, которым вы являетесь возвращение немного запутанно. Вы отображаете purchases
, но затем возвращаете кнопку add
, если это последняя покупка. Кнопка добавления не связана с отображением покупок, поэтому определите ее отдельно:
return (
<div>
// Map purchases
{purchases.map(purchase => (
// The JSX for purchases is defined here, not in state
<div key={purchase.id}>
<button onClick={() => removePurchase(purchase.id)}>
delete
</button>
Item number {purchase.id}
</div>
))}
// An add button at the end of the list of purchases
<button onClick={() => addNewPurchase()}>add</button>
</div>
)
Поскольку мы не должны хранить JSX в состоянии, в операторе return мы превращаем наши значения состояния в JSX.
Не давайте сбивающих с толку имен для функций установки.
Вы создали переменную состояния counter
и назвали функцию установки increment
. Это вводит в заблуждение - функция increment
не увеличивает счетчик, а устанавливает счетчик . Если я вызываю increment(0)
, счетчик не увеличивается, он устанавливается на 0.
Соответствует функциям установки имен. В сообществе React принято считать, что функция setter имеет то же имя, что и переменная, которую она устанавливает, с префиксом слова "set" . Другими словами, ваше значение состояния counter
, поэтому ваша функция установки должна называться setCounter
. Это точное и наглядное описание того, что делает функция:
const [counter, setCounter] = useState(0)
Состояние обновляется асинхронно - не обрабатывайте его синхронно
В функции addNewPurchase
вы получаете:
increment(counter + 1)
const uniqueId = `purchase${counter}`
Это не будет работать так, как вы ожидаете. Например:
const [myVal, setMyVal] = useState(0)
const updateMyVal = () => {
console.log(myVal)
setMyVal(1)
console.log(myVal)
}
Рассмотрим приведенный выше пример. Первый console.log(myVal)
будет записывать 0
в консоль. Что вы ожидаете второго console.log(myVal)
, чтобы войти? Вы можете ожидать 1
, но на самом деле он также регистрирует 0
.
Состояние не обновляется, пока функция не завершит выполнение и компонент не выполнит повторную визуализацию, поэтому значение myVal
никогда не изменится частично через функцию. Она остается неизменной для всей функции.
В вашем случае вы создаете идентификатор со значением old , равным counter
.
. компонент
Вот обновленная версия вашего компонента:
const InvestmentSimulator = () => {
// Use sensible setter function naming
const [counter, setCounter] = useState(0)
// Don't store JSX in state
const [purchases, setPurchases] = useState([])
const addNewPurchase = () => {
setCounter(prev => prev + 1)
setPurchases(prev => [...prev, { id: `purchase${counter + 1}` }])
}
const removePurchase = id => {
setPurchases(prev => prev.filter(p => p.id !== id))
}
// Keep your JSX simple
return (
<div>
{purchases.map(purchase => (
<div key={purchase.id}>
<button onClick={() => removePurchase(purchase.id)}>
delete
</button>
Item number {purchase.id}
</div>
))}
<button onClick={() => addNewPurchase()}>add</button>
</div>
)
}
Заключительные мысли
Даже с этими изменениями компонент по-прежнему требует -design.
Например, не рекомендуется использовать счетчик для создания уникальных идентификаторов. Если счетчик сброшен, элементы будут иметь одинаковый идентификатор. Я ожидаю, что каждый из этих элементов в конечном итоге будет хранить больше данных, чем просто идентификатор, поэтому присваивайте им уникальный идентификатор, связанный с элементом, а не с его местом в списке.