admin管理员组

文章数量:1303335

I need your help with an issue and I tried to find a solution for it, but unable to find it.

I have an array with data that I display in a table, then each row has an edit button. But depending on specific data, I show or hide the edit button.

This is an example of the code I use:

export default function SettingView() {
    const [editRates, setEditRates] = useState<boolean>(false);
    const [editNetRates, setEditNetRates] = useState<boolean>(false);

    function renderTableData() {
        return (
            <>
                <table
                    headings={[
                        {content: 'Name'},
                        {content: 'Inherited value'},
                        {content: 'Overwrite value'},
                        {
                            content: 'Edit',
                            align: 'center',
                        }
                    ]}
                    rows={getRows(viewData)}
                />
            </>
        )
    }

    function getRows(viewOutput) {
        const rows: Array<Row> = [];

        if (viewOutput !== undefined) {
        
            const sortFunc = (a:string, b:string) => {
            const fa = a.toLowerCase(),
                  fb = b.toLowerCase();
            if (fa < fb) {
                return -1;
            }
            if (fa > fb) {
                return 1;
            }
            return 0;
        }
        const sortedRows = Object.values(viewOutput).sort((a, b) => sortFunc(a.field.name, b.field.name));

        for (let item of sortedRows) {
            checkValue(item);
            rows.push({

                cells: [

                    { content: (<SettingTitle title={item.field.name} description={item.field.description} isFiltered={item.field.name === filtered_item} />)
                    },
                    { content: item.default_value
                        ? item.default_value
                        :  "",
                    },
                    { content: item.override_value
                        ? item.override_value
                        : "",
                    },
                    { content: showEditButton(item)
                        ?  <Button
                                text="Edit"
                                variant="primary"
                                destructive={false}
                                type="button"
                                size="medium"
                                loading={false}
                                wide={false}
                                onClick={() => {}}
                            />
                        : ("")
                    },

                ]

            });

        }
    }

    function showEditButton(item: any) {
        if (item.is_editable) {
            if (item.field.name === 'name1') {
                if (editRates) {
                    return true;
                }
                return false;
            } else if (item.field.name === 'name2') {
                if (editNetRates) {
                    return true;
                }
                return false;
            }
            return true;
        }        
        return false;
    }

    function checkValue(item: any) {
        if (item.field.name === 'specifc_name') {
            if (!item.has_override && item.value === 'none') {
                setEditRates(true);
                setEditNetRates(false);
            } else if (item.has_override && item.value !== 'none') {
                setEditRates(false);
                setEditNetRates(true);
            }
        }

    }

}

The error I get is: too many re-renders.

If I remove the setEditRates and setEditNetRates parts from the checkValue function then I get no error, but of course both useStates are not changing.

I need your help with an issue and I tried to find a solution for it, but unable to find it.

I have an array with data that I display in a table, then each row has an edit button. But depending on specific data, I show or hide the edit button.

This is an example of the code I use:

export default function SettingView() {
    const [editRates, setEditRates] = useState<boolean>(false);
    const [editNetRates, setEditNetRates] = useState<boolean>(false);

    function renderTableData() {
        return (
            <>
                <table
                    headings={[
                        {content: 'Name'},
                        {content: 'Inherited value'},
                        {content: 'Overwrite value'},
                        {
                            content: 'Edit',
                            align: 'center',
                        }
                    ]}
                    rows={getRows(viewData)}
                />
            </>
        )
    }

    function getRows(viewOutput) {
        const rows: Array<Row> = [];

        if (viewOutput !== undefined) {
        
            const sortFunc = (a:string, b:string) => {
            const fa = a.toLowerCase(),
                  fb = b.toLowerCase();
            if (fa < fb) {
                return -1;
            }
            if (fa > fb) {
                return 1;
            }
            return 0;
        }
        const sortedRows = Object.values(viewOutput).sort((a, b) => sortFunc(a.field.name, b.field.name));

        for (let item of sortedRows) {
            checkValue(item);
            rows.push({

                cells: [

                    { content: (<SettingTitle title={item.field.name} description={item.field.description} isFiltered={item.field.name === filtered_item} />)
                    },
                    { content: item.default_value
                        ? item.default_value
                        :  "",
                    },
                    { content: item.override_value
                        ? item.override_value
                        : "",
                    },
                    { content: showEditButton(item)
                        ?  <Button
                                text="Edit"
                                variant="primary"
                                destructive={false}
                                type="button"
                                size="medium"
                                loading={false}
                                wide={false}
                                onClick={() => {}}
                            />
                        : ("")
                    },

                ]

            });

        }
    }

    function showEditButton(item: any) {
        if (item.is_editable) {
            if (item.field.name === 'name1') {
                if (editRates) {
                    return true;
                }
                return false;
            } else if (item.field.name === 'name2') {
                if (editNetRates) {
                    return true;
                }
                return false;
            }
            return true;
        }        
        return false;
    }

    function checkValue(item: any) {
        if (item.field.name === 'specifc_name') {
            if (!item.has_override && item.value === 'none') {
                setEditRates(true);
                setEditNetRates(false);
            } else if (item.has_override && item.value !== 'none') {
                setEditRates(false);
                setEditNetRates(true);
            }
        }

    }

}

The error I get is: too many re-renders.

If I remove the setEditRates and setEditNetRates parts from the checkValue function then I get no error, but of course both useStates are not changing.

Share edited Feb 10 at 18:50 DarkBee 15.6k8 gold badges72 silver badges116 bronze badges asked Feb 10 at 18:46 udartsudarts 8881 gold badge9 silver badges30 bronze badges 10
  • What triggers these functions? Can you construct a minimal reproducible example which would demonstrate the problem? Though updating the same state values like that repeatedly in a loop is certainly uncommon, and probably not what you really want to do. If the goal is to set them to whatever values they need to be on only the last iteration of the loop then why not do that? Or calculate the intended result in the loop and then update the state after the loop with that intended result? – David Commented Feb 10 at 18:57
  • @David did you voted for close? If so, why? I can't provide a good example, as the original is data sensitive (also with custom components) and is difficult to create an example. – udarts Commented Feb 10 at 19:29
  • Without an example we can really only guess. And the answer below is as good a guess as any. If you're invoking these functions on every render and updating state on every render then that's an infinite re-render loop. The solution, however you structure it, is to not update state on every render. – David Commented Feb 10 at 19:30
  • @David, I have tried to move it outside the for loop and add the sortedRows in the checkValue function directly, then do a for loop in that function, but the result is the same. I have been looking into using useMemo or useCallBack instead, but not sure if that would solve it. – udarts Commented Feb 10 at 21:50
  • Hello, I was trying to run this code locally, due to missing the following, it does not happen. Line 20:25: 'viewData' is not defined no-undef Line 51:18: 'SettingTitle' is not defined react/jsx-no-undef Line 54:51: 'filtered_item' is not defined no-undef Line 62:18: 'Button' is not defined react/jsx-no-undef. – WeDoTheBest4You Commented Feb 11 at 4:31
 |  Show 5 more comments

2 Answers 2

Reset to default 1

Every time you set a state the affected components will have to rerender. Every time you render your component you end up calling checkValue which sets a state and triggers another rendering.

Ok, I managed to fix it, by using a const and setting its value by calling a function which returns the value for the const. I called it outside the for loop, so preventing the error message.

This is kinda how I have it now (partially from the code above):

function getRows(viewOutput) {
    const rows: Array<Row> = [];
    const sortedRows = Object.values(viewOutput).sort((a, b) => sortFunc(a.field.name, b.field.name));
    const ratesValue = checkRates(sortedRows);

    for (let item for sortedRows) {
        ....
        { content: showEditButton(item, ratesValue)
            ?  <Button
                text="Edit"
                variant="primary"
                destructive={false}
                type="button"
                size="medium"
                loading={false}
                wide={false}
                onClick={() => {}}
              />
            : ("")
        },
    }
    return rows;
}

function showEditButton(item: any, editRates: any) {
    if (item.is_editable) {
        if (item.field.name === 'name1') {
            if (editRates === 'selRates') {
                return true;
            }
            return false;
        } else if (item.field.name === 'name2') {
            if (editRates === 'netRates') {
                return true;
            }
            return false;
        }
        return true;
    }        
    return false;
}

function checkRates(items: any) {
    for (let item for items) {
        if (item.field.name === 'setting_name') { // running only when this setting is found
            if (item.value === 'none') {
                return 'selRates';
            } else if (item.value !== 'none') {
                return 'netRates';
            }
        }
    }
}

This is kinda how I got it working.

本文标签: reactjsrerender error when using useState inside a for loopStack Overflow