Почему перехватчики реагируют на старые данные в обработчиках событий? - PullRequest
0 голосов
/ 08 апреля 2020

Я использую хуки для поддержания состояния массива, содержащего данные вместе с компонентом кнопки. Этот компонент имеет обработчик событий onClick.

const MyComponent = () => {
  const initialData = [{ "field": "foo", "button": getButton("foo") }];
  const [ data, setData ] = useState(initialData);

  const getButton = (entry) => <Button onClick={() => doStuff(entry)}>Hello</Button>

  const doStuff = (entry) => console.log(data)

  const addData = () => {
    const moreData = { "field": "bar", "button": getButton("bar") }
    setData([ ...data, moreData ])

    const evenMoreData = { "field": "roll", "button": getButton("roll") }
    setData([ ...data, evenMoreData ])
  }

  const myList = data.map(d => <>{d.field} {d.button}</>)

  return (
    <>
      {myList}
      <Button onClick={() => doStuff('foo')}>Hi</Button>
      <Button onClick={() => addData()}>Add Data</Button>
    </>
  )
}

Этот data корректно отображается в myList. Когда я нажимаю кнопку в foo, она вызывает doStuff, которая печатает data как пустой массив (как если бы это был моментальный снимок во времени до добавления foo). Если я нажимаю кнопку в bar, она печатает массив с foo. Если я нажимаю кнопку в roll, она печатает массив, содержащий только foo и bar.

Однако, если я добавлю идентичную кнопку за пределами myList, она может напечатать data правильно содержит все foo, bar и roll. Почему это так, и как я могу обойти это?

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

Ответы [ 2 ]

0 голосов
/ 08 апреля 2020

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

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

Также при добавлении дополнительных данных используйте шаблон обратного вызова средства обновления состояния

const MyComponent = () => {
  const initialData = [{ "field": "foo"}];
  const [ data, setData ] = React.useState(initialData);

  const getButton = (entry) => <button onClick={() => doStuff(entry)}>Hello</button>

  const doStuff = (entry) => console.log(data)

  const addData = () => {
    const moreData = { "field": "bar" }
    setData(prev=> ([ ...prev, moreData ]))

    const evenMoreData = { "field": "roll"}
    setData(prev => ([ ...prev, evenMoreData ]))
  }

  const myList = data.map(d => <React.Fragment>{d.field} {getButton(d.field)}</React.Fragment>)

  return (
    <React.Fragment>
      {myList}
      <button onClick={() => doStuff('foo')}>Hi</button>
      <button onClick={() => addData()}>Add Data</button>
    </React.Fragment>
  )
}

ReactDOM.render(<MyComponent />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app"/>
0 голосов
/ 08 апреля 2020

Вы можете превратить Button в компонент и сделать что-то подобное:

const initialData = { field: "foo" };

function App() {
  
  const [data,setData] = React.useState(initialData);
  
  function toggleButton() {
    setData((prevState) => {
      return prevState.field === "foo" ?
        {field: "bar"}
      : {field: "foo"}
    });
  }
  
  
  return(
    <div>
      <div>
        Current data.field: {data.field}
        <button onClick={toggleButton}>
            Toggle button
        </button>
      </div>
      <Button value={data.field}/>
    </div>
  );
}


function Button(props) {
  function doStuff() {
    console.log(props.value);
  }
  return(
    <button onClick={doStuff}>Do Stuff {props.value}</button>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...