background
A searchable, actionable list page developed daily.
Page structure
Form rendering filter item +Table shows the specific structure of data as shown below:
Code implementation
Filter filter changes trigger onChange to store filter parameters in state.
Const [reqParams, setReqParams] = useState<any>({pageNo: 1, pageSize: Const onChange = (params) => {setReqParams({... reqParams, ... Const fetchList = async () => {// console.log(reqParams) const res: any = await axios.post(URL.queryList, { ... reqParams }) const { list, pageSize, pageNo, total } = (res && res.data) || {} setDataSource(list || []) setPagination({ ... pagination, current: pageNo, total, pageSize }) }Copy the code
The operation column of the table encapsulates a component (Operate). After different operations such as “delete, edit” are displayed according to the parameter tabKey, the data should be refreshed to ensure the accuracy and real-time performance of the data.
// COLUMNS as defined by the table COLUMNS const COLUMNS = useMemo(() => {const newColuumns: Any [] = COLUMNS. Concat () // if (getPermission(permission.all_action)) {newcoluumns.push ({title: 'operation ', dataIndex: 'actions', key: 'actions', fixed: 'right', width: 150, align: 'center', render: (text, record) => ( <Operate data={record} tabKey={tabKey} handleRefresh={fetchList} reqParams={reqParams} /> ), }) // } return newColuumns }, [tabKey])Copy the code
Part of the operation component code: according to the business logic and permission points assigned to the operation button, after the operation through the external passed handleRefresh function refresh table data
const Operate: FC<OperateProps> = ({ data = {}, tabKey, handleRefresh }) => { const { auditStatus, OrgId} = data const handleDel = (event) => {modal. confirm({title: 'Confirm ${event}? `, onOk: () => {// do something message.success(' ${event} succeeded ') handleRefresh()}, }) } const btns = useMemo(() => { const newBtns: Element[] = [] if (tabKey === tabs_map.create && getPermission(permission.del)) {newTbs.push ({event: 'delete ', func: handleDel, }) } return newBtns }, [auditStatus, tabKey]) return ( <> <Space> {btns.map((element: Element) => ( <Button key={element.event} type='link' onClick={() => { element.func(element.event) }} > {element.event} </Button> ))} </Space> </> ) }Copy the code
Phenomenon of the problem
When handleRefresh calls the external fetchList, the reqParams parameter in the fetchList function is not the reqParams in the current (real-time) state, but the initial state of the page after it is first mounted. {pageNo:1,pageSize:10})
Question why
The reasons are: I used useMemo to define columns. The second argument to useMemo only specifies the taKeys that affect BTNS, assuming that the fetchList is triggered when handleRefresh is called. The reqParms in the fetchList function fetch the reqParams in the current (at this point) state. That’s not the case. When the second useMemo argument only specifies the tabKey, the dependency is not changed during the next rendering and useMemo does not call the first useMemo parameter compute but returns the memory of the first rendering. So explains why the reqParams of the handleRefresh function call is the initial value!!
Problem solving
Just add the dependencies with reqParams when defining columns. As follows:
// COLUMNS as defined by the table COLUMNS const COLUMNS = useMemo(() => {const newColuumns: Any [] = COLUMNS. Concat () // if (getPermission(permission.all_action)) {newcoluumns.push ({title: 'operation ', dataIndex: 'actions', key: 'actions', fixed: 'right', width: 150, align: 'center', render: (text, record) => ( <Operate data={record} tabKey={tabKey} handleRefresh={fetchList} reqParams={reqParams} /> ), }) // } return newColuumns }, [tabKey, reqParams])Copy the code
Summary of useMemo usage
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
UseMemo () is a built-in React hook that takes 2 arguments — a computeExpensiveValue calculation result and a depedenCies function that depends on the array: UseMemo (computeExpensiveValue, Dependencies) calls computeExpensiveValue during initial rendering, remembers the calculation result and returns it to the component.
If the dependency has not changed during the next rendering, useMemo() does not call computeExpensiveValue but returns the remembered value.
But if the dependency changes during the re-rendering process, useMemo() calls computeExpensiveValue, remembers the new value, and returns it.
This is the essence of the useMemo() hook.
If you compute callbacks using props or state values, be sure to indicate those values as dependencies.
The use of useCallback and its differences from useMemo
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
UseCallback () is a more specialized hook than useMemo(), returning a Memoized callback function for memorizing callback instances.
Passing the inline callback function and the array of dependencies as arguments to useCallback returns the Memoized version of the callback function, which is updated only when a dependency changes. This is useful when you pass callback data to child components that are optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).
UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps).
import { useCallback } from 'react';
function MyComponent({ prop }) {
const callback = () => {
return 'Result';
};
const memoizedCallback = useCallback(callback, [prop]);
return <ChildComponent callback={memoizedCallback} />;
}
Copy the code
In the example above, useCallback(() => {… }, [prop]) return the same function instance as long as the prop dependency is the same.
conclusion
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); Hook. Given the same [A, b] dependency, once remembered, the hook returns the remembered value instead of calling computeExpensiveValue (a, b). UseEffect, useMemo, and useCallback are essentially the same. If the dependency has not changed, the prefunction will not be repeated.