react hook useState na função AgGridColumn onCellClicked
Atualmente, estou usando componentes de reação funcionais com gancho de reação useStatee um AgGridReactcomponente.
Estou exibindo um AgGridReacte coloco uma onCellClickedfunção em a AgGridColumn. Até agora tudo está funcionando. Na onCellClickedfunção, quero atualizar meu estado e fazer algo dependendo de seu valor atual.
Aqui está o problema:
se eu quiser usar meu estado get / set ( useStatehook) dentro da onCellClickedfunção, ele não está funcionando conforme o esperado. Por algum motivo, não consigo atualizar meu estado.
Em um componente da classe react está funcionando.
EDIT: Eu experimentei por um tempo e descobri que na onCellClickedfunção eu tenho apenas o valor padrão em meuTexto. Posso atualizá-lo uma vez. Se eu onCellClickedenviar spam para a função, ela acrescentará o texto novamente ao valor padrão de useState("default myText");. Eu esperaria que a string ficasse mais longa com a frequência com que eu clico no celular. Exatamente como no meu exemplo de componente de classe. Se eu usar um botão simples fora do, AgGridReact <button onClick={() => setMyText(myText + ", test ")}>add something to myText state</button>ele está funcionando conforme o esperado, a string fica mais longa a cada vez que clico no meu <button>. Se eu alterar o estado de meuTexto pelo <button>lado de fora do AgGridReacte clicar na função de célula novamente, o estado definido anteriormente por meio de meu <button>será perdido.
Exemplo de componente de gancho de reação:
import React, { useState } from 'react';
import { AgGridColumn, AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
function App() {
const [myText, setMyText] = useState("default myText");
const [todoListRowData, setTodoListRowData] = useState([]);
// ....fetch data and set the todoListRowData state....
const myCellClickFunction = (params, x) => {
// here is the problem:
// no matter how often I click in the cell myText is every time the default value 'default myText'
// EDIT: I found out I can update the state here but only from the initial default value once, myText is on every cell click again "default myText" and will be concatenated with "hookCellClicked". So every time I click this cell the state gets again "default myTexthookCellClicked"
console.log(myText);
setMyText(myText + "hookCellClicked");
}
return (
<div className="ag-theme-alpine" style={{ height: '600px', width: '100%' }}>
<AgGridReact rowData={todoListRowData} >
<AgGridColumn headerName="ID" field="id" maxWidth="50"></AgGridColumn>
<AgGridColumn headerName="UserId" field="userId" maxWidth="85"></AgGridColumn>
<AgGridColumn headerName="Title" field="title" minWidth="555"></AgGridColumn>
<AgGridColumn headerName="completed" field="completed"></AgGridColumn>
<AgGridColumn headerName="Testcol" onCellClicked={(params) => myCellClickFunction(params)}></AgGridColumn>
</AgGridReact>
</div>
}
export default App;
Se eu fizer exatamente a mesma coisa em um componente de classe, está funcionando bem.
Exemplo de componente de classe:
import React from 'react';
import { AgGridColumn, AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
class MyClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
myClassComponentRowData: [],
testState: "defaul testState"
};
}
// ....fetch data and set ag grid rowData state....
handleCellClick = (params) => {
// here everything works just fine and as expected
// every time I click on the cell the state testState updates and it is added ", handleCellClick" every time
console.log(this.state.testState);
this.setState({testState: this.state.testState + ", handleCellClick"});
}
render() {
return <div className="ag-theme-alpine" style={{ height: '600px', width: '100%' }}>
<AgGridReact rowData={this.state.myClassComponentRowData} >
<AgGridColumn headerName="ID" field="id" maxWidth="50"></AgGridColumn>
<AgGridColumn headerName="UserId" field="userId" maxWidth="85"></AgGridColumn>
<AgGridColumn headerName="Title" field="title" minWidth="555"></AgGridColumn>
<AgGridColumn headerName="completed" field="completed"></AgGridColumn>
<AgGridColumn headerName="Testcol" onCellClicked={(params) => this.handleCellClick(params)}></AgGridColumn>
</AgGridReact>
</div>
}
}
export default MyClassComponent;
Estou fazendo algo errado? Quero usar um componente funcional com ganchos de reação.
Respostas
Não há nada de errado com o código em sua pergunta, exceto que o retorno de chamada faz myCellClickFunctionreferência ao antigo estado myTextque foi capturado na chamada de renderização anterior. Se você efetuar login no método render, poderá ver que o estado está atualizado corretamente. Esse problema é chamado de fechamento obsoleto .
function App() {
const [myText, setMyText] = useState("default myText");
const [todoListRowData, setTodoListRowData] = useState(rowData);
console.log("render", myText); // prints the latest myText state
...
}
Você pode ver minha outra resposta aqui sobre como obter o estado mais recente no retorno de chamada ao usar o gancho React. Aqui está um exemplo para você experimentar.
function useExtendedState(initialState) {
const [state, setState] = React.useState(initialState);
const getLatestState = () => {
return new Promise((resolve, reject) => {
setState((s) => {
resolve(s);
return s;
});
});
};
return [state, setState, getLatestState];
}
function App() {
const [myText, setMyText, getLatestMyText] = useExtendedState(
"default myText"
);
const myCellClickFunction = async (params) => {
setMyText(params.value);
const text = await getLatestMyText();
console.log("get latest state in callback", text);
};
...
}
Demonstração ao vivo