React useState не обновляется из-за useRef

У меня возникла очень странная проблема с моим кодом реакции: useState не обновляет представление, и после того, как буквально все испробовали, проблема все еще существует. Я сделал простой код, чтобы объяснить проблему:

function(){
  
    const [enterJob, setEnterJob] = useState(false);
    const [jobSelection, setJobSelection] = useState(Array(someList.length).fill(false));
    const jobRef = useRef();

    const handleJobClick = i => {
        const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
        let c = jobSelection;
        c[n] = !c[n];
        setJobSelection(c);
    };
  
  
  const handleMouse = (e) =>{
        if (!jobRef.current.contains(e.target)){
            setEnterJob(false);
        };
    };
  
  useEffect(() => {
        window.addEventListener("mousedown", handleMouse);
        return () => window.removeEventListener("mousedown", handleMouse);
    });
  return(
        <div ref={jobRef}>
           <input onFocus={()=> setEnterJob(true)} />
           <div style={{display: `${enterJob ? 'flex' : 'none'}`}} >
               <ul>
                { someList.map((item,index)=> 
                <li id={`${index}`} onClick={handleJobClick}> {jobSelection[index] ? item : "you clicked on the button"} </li> )}
               </ul>
           </div>
        </div>
  
  )


}

Некоторые пояснения: я использую UseEffect и useRef для создания раскрывающегося меню, которое исчезает, когда вы щелкаете мышью за пределами контейнера. Теперь, когда я хочу щелкнуть значение этого раскрывающегося меню, оно не обновляет DOM, пока я использую useState для обновления значения строки, ответственной за изменение.

Заранее спасибо, Charbel


person Charbel Imad    schedule 17.11.2020    source источник


Ответы (2)


Проблема в том, что вы изменяете свой jobSelection вместо создания нового объекта. И response пропустит повторную визуализацию, если объекты имеют ту же ссылку, что и раньше:

 const handleJobClick = i => {
        const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
        let c = [...jobSelection]; // Create a new array
        c[n] = !c[n];
        setJobSelection(c);
    };
person Domino987    schedule 17.11.2020
comment
Отлично, большое спасибо, что это было! Мне нужно будет узнать немного больше о мутациях. - person Charbel Imad; 18.11.2020

Проблемы

Если я понимаю вашу проблему, я считаю, что это связано с тем, что вы напрямую мутируете свое состояние.

const handleJobClick = i => {
    const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
    let c = jobSelection;
    c[n] = !c[n]; // <-- mutation!
    setJobSelection(c);
};

Вам также не хватает клавиш реагирования на элементы сопоставленного списка.

Решение

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

Я предлагаю:

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

Код

const handleJobClick = index => () => {
  setJobSelection(jobSelection => jobSelection.map(
    (selection, i) => index === i ? !selection : selection // <-- toggle selection at matched index
  );
};

...

<ul>
  {someList.map((item, index)=> (
    <li
     key={index} // <-- index as react key, ok since not adding/removing/sorting jobs
     onClick={handleJobClick(index)} // <-- pass index to handler
    >
      {jobSelection[index] ? item : "you clicked on the button"}
    </li>
  ))}
</ul>
person Drew Reese    schedule 17.11.2020
comment
Идеально ! Большое спасибо, что это было. Решение @ Domino987 немного проще, но это гораздо проще для понимания. - person Charbel Imad; 18.11.2020
comment
Нет проблем, то же самое касается объектов, массивов и даже строк и логических значений. Если у вас есть какие-либо вопросы, звоните мне - person Domino987; 18.11.2020
comment
@CharbelImad Конечно. Я заметил еще пару проблем, поэтому обновил свой ответ, чтобы помочь их решить. - person Drew Reese; 18.11.2020
comment
Спасибо за это исправление. У меня странная проблема при передаче значения индекса таким образом @ drew-reese: я получаю ошибку рендеринга бесконечного цикла. Я посмотрю повнимательнее и вернусь к вам. - person Charbel Imad; 18.11.2020
comment
@CharbelImad Вы меняли подпись функции handleJobClick? handleJobClick = index => () => {...}? Это каррированная функция, которая принимает индекс и возвращает функцию для использования в качестве обратного вызова, т.е. она принимает событие, но, поскольку нас не волнует ничто в объекте события, нам не нужно назначать это идентификатор. - person Drew Reese; 18.11.2020
comment
Ага, это та же функция. Должен признать, это странно. - person Charbel Imad; 18.11.2020
comment
@CharbelImad Если проблема все еще существует, если вы можете создать работающий код и ящик с этой проблемой, я могу помочь отладить ее. - person Drew Reese; 18.11.2020
comment
@DrewReese, похоже, его пока нет. Еще раз спасибо за всех! - person Charbel Imad; 18.11.2020
comment
@CharbelImad Не забывайте голосовать за полезные и / или полезные ответы и объяснения. Ваше здоровье. - person Drew Reese; 18.11.2020