Фильтровать массив без переопределения

#javascript #reactjs #react-hooks

#javascript #reactjs #реагировать-крючки

Вопрос:

Я пытаюсь отфильтровать этот массив при изменении входного «фильтра», но когда я пытаюсь ввести, он хорошо фильтрует, но переопределяет исходный список. Итак, как я могу отфильтровать список при вводе и вернуться к исходному списку при удалении значения фильтра?

 const [persons, setPersons] = useState([
        { name: 'Arto Hellas', number: '040-123456' },
        { name: 'Ada Lovelace', number: '39-44-5323523' },
        { name: 'Dan Abramov', number: '12-43-234345' },
        { name: 'Mary Poppendieck', number: '39-23-6423122' }
    ])

const [filter, setFilter] = useState('')

const handleFilter = e => {
    setFilter(e.target.value)
    const personsFiltered = persons.filter(({ name }) => name.includes(filter))
    if (personsFiltered.length > 0)
        setPersons(personsFiltered)
}

return (
    <div>
        <input value={filter} onChange={handleFilter} />
        {persons.map((person, index) => <p key={index}>{person.name} {person.number}</p>)}
    </div>
)
  

Комментарии:

1. Где ваш код рендеринга для лиц? Вы могли бы более легко сделать это, выполнив фильтр в функции рендеринга, а не изменяя список

2. Не храните отфильтрованный массив в состоянии, которое может быть вычислено из того, что у вас уже есть в состоянии. Если вы обнаружите, что фильтрация по требованию является узким местом в производительности, используйте useMemo для запоминания результата.

3. У вас должна быть копия оригинала для выполнения фильтрации. когда вам это нужно, просто сбросьте его с оригинальным.

4. Спасибо за обновление вашего кода — пожалуйста, смотрите Мой ответ о том, как добиться этого как часть кода, который вы уже визуализируете.

Ответ №1:

Вы должны быть в состоянии достичь желаемого, отфильтровав свои результаты как часть рендеринга. Вы обновляете свое filter состояние, поэтому повторный рендеринг должен запускаться, как и ожидалось, при событии:

РЕДАКТИРОВАТЬ: я вижу, что вы обновили свой вопрос своим кодом рендеринга. Вы должны иметь возможность просто применить filter функцию к тому, что у вас уже есть:

 {persons.filter(({ name }) => name.includes(filter)).map((person, index) => <p key={index}>{person.name} {person.number}</p>)}
  

Ответ №2:

Вы должны поддерживать дополнительное состояние для отфильтрованных значений.

Просто используй другой useState

 const [persons, setPersons] = useState([
  { name: 'Arto Hellas', number: '040-123456' },
  { name: 'Ada Lovelace', number: '39-44-5323523' },
  { name: 'Dan Abramov', number: '12-43-234345' },
  { name: 'Mary Poppendieck', number: '39-23-6423122' }
])

// use additional state here
const [personsFiltered, setPersonsFiltered] = useState(persons);

const [filter, setFilter] = useState('')

const handleFilter = e => {
setFilter(e.target.value)
const personsFiltered = persons.filter(({ name }) => name.includes(filter))
if (personsFiltered.length > 0)
  // set the additional state here
  setPersonsFiltered(personsFiltered)
}

return(<input value={filter} onChange={handleFilter} />)
  

Комментарии:

1. Использование другого состояния для этого, на мой взгляд, не требуется — оно просто оставляет другое состояние для управления и быть в курсе, когда мы можем выполнять логику фильтрации как часть рендеринга.

2. у вас все еще должен быть где-то исходный список, и каждое изменение ввода должно сверяться с исходным списком, а не с уже отфильтрованным списком. обычно этот исходный список поступает из реквизитов, вместо того, чтобы определять его внутри компонента

3. Однако вы можете выполнить фильтрацию как часть рендеринга и не беспокоиться ни об одной из этих проблем.

4. В некоторых случаях это верно. Все зависит от того, выполняете ли вы другие действия с отфильтрованным массивом перед рендерингом компонента.