Реагируют крючки — состояние обновления useEffect, но обновленное состояние не отображается повторно

#javascript #reactjs #react-hooks #rendering #use-effect

Вопрос:

Предыстория проблемы

У меня проблема с моим крючком для реакции.

В принципе, я хочу повторно отобразить свои <li> компоненты (названные elementsToDisplay в приведенном ниже коде), когда пользователь изменяет порядок их отображения или когда пользователь вводит строку поиска в элемент ввода текста для их фильтрации.

 //...import statements here...

function myHook(props) {
    const [searchString, setSearchString] = useState('') // Initially empty searchstirng
    const [order, setOrder] = useState('order1') // Ordering is initially order1
    const [myObjects, setObjects] = useState(props.myObjects) // The objects needed to create the elements to display
    const [elementsToDisplay, setElementsToDisplay] = useState([]) // The <li> elements to display (based on myObjects)
    
     /* 
        IF THE SEARCHSTING OR THE ORDER GET CHANGED BY THE USER, CHANGE MY OBJECTS
    */
   useEffect(() => {
       var myObjectsCopy = [... props.myObjects]
       myObjectsCopy.sort((a,b) => {
           // Ordina in base a cosa l' utente ha selezionato dal sortByButton.
           if (order == 'order1'){
               // return some ordering
            }
            else if (order == 'order2'){
                // return some ordering
            }
            else if (order == 'order3'){
                // return some ordering
            }
            else{
                // return some ordering
            }
        })
        var newObjects = myObjectsCopy.filter(item => {
             // filter based on searchsting
             return item.productCode.toLowerCase().indexOf(searchString.toLowerCase()) >= 0
         })
       setObjects(newObjects)
       console.log(myObjects[0]) // NOTE here myObjects get updated as expected everytime
   }, [order, searchString])


   /*
      When myObjects change, so the <li> elements to display should (end they do!).
   */
    useEffect(()=>{
        var elements = get_elements_to_display()
        setElementsToDisplay(elements)
        console.log(elementsToDisplay[0]) // NOTE, elementsToDisplay change as expected!!!!!!
    },[myObjects])



    /*
       Function that creates the <li> elements to display based on myObjects. NOTE works fine
    */
     function get_elements_to_display(){
        return myObjects.map((item, index) => {
            var competitors = item.competitors
            return(
                <li key={index}>
                    <span className='produt-separator'/>
                    <ProductTable item={item} index={index} />
                </li>
            )
        })
    }

    

    /*
       Change the ordering to display the elements. NOTE Work as expected!!!
    */
    function handleChangeOrder(e, value) {
        e.preventDefault();
        console.log(value) // NOTE the right ordering gets passed
        setOrder(value)
        console.log(order) // NOTE order gets updated as expected
    }


   
    /*
       Render the component
    */
     return (
            <div>
                <h1 className='page-title'>My Title</h1>
                <input className="search-input" type='text' 
                    onChange={e => setSearchString(e.target.value)} 
                    placeholder='Cerca il toner o cartuccia per nome...'></input>
                <span width="8px"/>
                <SortByButton handleClick={handleChangeOrder}/>
                <AddProductPopUp/>
                <ul>
                    {elementsToDisplay}
                </ul>
            </div>
        )

}


 

Проблема

Проблема в том, что все мои переменные состояния изменяются, как и ожидалось, но отображаемые <li> компоненты остаются прежними. Это странно, так как они хранятся в состоянии elementsToDisplay , и я вижу, что они обновляются всякий раз, когда я выбираю новый заказ или пытаюсь отфильтровать свои элементы, вводя строку поиска.

Можете ли вы увидеть, не делаю ли я что-то не так, из опубликованного мной примера кода?

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

1. Вы пробовали изменить ключ списка <li key={index}> на какой-то другой уникальный идентификатор, кроме index ? Кроме того, можем ли мы получить CodeSandbox для репликации проблемы, с которой вы столкнулись?

2. @AreebKhan Ты мой спаситель. Смена key= реквизита на что-то уникальное сделала свое дело. Однако я не понимаю, в чем тут логика. Не могли бы вы опубликовать ответ с некоторым объяснением, чтобы я мог установить его в качестве принятого ответа?

3. Да, конечно. Реакция использует ключи для определения того, какие элементы в списке изменились. Поскольку вы используете индекс в качестве ключа в нестатических экземплярах, а затем сортируете массив, из которого выполняете рендеринг, порядок индексов остается прежним. (1/2)

4. Другими словами, перед сортировкой динамического массива [y,x,z] и использованием их индекса в качестве ключа React в основном связывается y с 0, x с 1 и z с 2. После сортировки массив становится [x,y,z] и порядок элементов изменился, но элемент с индексом 0 по-прежнему первый! Теперь, поскольку React использует ключи для обнаружения изменений, и мы сохранили индексы в качестве ключей (порядок которых не меняется до и после сортировки), React не отображает изменения. Источник (2/2)

Ответ №1:

(Из комментариев)

Изменение ключа списка <li key={index}> на какой-либо уникальный идентификатор, отличный index от того, исправит это.

Реакция использует ключи для определения того, какие элементы в списке изменились. Поскольку вы используете индекс в качестве ключа в нестатических экземплярах, а затем сортируете массив, из которого выполняете рендеринг, порядок индексов остается прежним.

Ответ №2:

На этот вопрос был дан ответ в комментариях.