Home > front end >  How to escape infinite loop of useState hook in React?
How to escape infinite loop of useState hook in React?

Time:12-22

I am new to react and learning about hooks but I cannot escape from useState at second execution.

My caller function is calling another component called QueryPanel with parameter:

const [availables, setAvailable] = useState<string[]>([]);
useEffect(() => {
    context.services.records.getAvailablesList()
        .then((resp) => {
            if(resp != undefined) {
                setAvailables(resp);
            }
        });
}, []);

return <Panel1 text={availables}></Panel1>;

So Panel1 is opening with a string array or an empty array. What I want to do is that show dropdown and if the parameter is not empty then show first item as default item or if the parameter list is empty then show a string as a default value "No option available"

export const QueryPanel = (props: any) => {
    const t = useTrans();
    const context = useContext(AppContext);
    const [form] = Form.useForm<Store>();
    const [defaultValue, setDefaultValue] = useState("asd");

    const { Option } = Select;

    const onFinish = (values: Store) => {
        const queryParams: Query = {
            system: values.systemType,
            startDate: values.startDate,
            endDate: values.endDate,
            startFrequency: values.startFrequency,
            endFrequency: values.endFrequency
        };
        context.services.records.query(queryParams);
    };

    const validateOnFormChange = () => {
        form.validateFields();
    };

    const onFinishFailed = (errorInfo: ValidateErrorEntity) => {
        console.log('Failed:', errorInfo);
    };

// useEffect(() => {
//
//     if (props.text.length !== 0) {
//         setDefaultValue(props.text[0]);
//     }
//     else {
//         setDefaultValue("No option available");
//     }
// }, []);

// if (props.text.length > 0) {
//     setDefaultDbValue(props.text[0]);
// }
// else {
//     let defaultDropDownOption:string = "No Db Available";
//     setDefaultDbValue(defaultDropDownOption);
// }

My if-else is stuck in infinite loop because whenever I set default value to a value then it goes to same if statement again. How can I escape from it without using extra field ? useEffect is commented out here and the weird thing is when i check the parameter prop.text it is undefined at beginning and thats why the code is not going inside if-else in useEffect and then at second iteration the prop.text is coming as true.

I have tried something with useEffect hook but did not work:

useEffect(() => {
        if (props.text.length > 0) {
            setDefaultValue(props.text[0]);
        }
    }, [defaultValue]);

But this time default db is coming empty

CodePudding user response:

I believe that one way would be to do :

useEffect(() => {
        if (props.text.length > 0 && props.text[0] !== defaultValue) {
            setDefaultValue(props.text[0]);
        }
    }, [defaultValue]);

By doing so, you will update the defaultValue only when it differs from props.text[0]

CodePudding user response:

The infinite loop is created because React will rerender your <Panel1 /> component whenever the state of the component changes.

In the body of your component you have this if ... else statement sitting without wrapping it in a hook:

if (props.text.length > 0) {
    setDefaultDbValue(props.text[0]);
} else {
    let defaultDropDownOption:string = "No Db Available";
    setDefaultDbValue(defaultDropDownOption);
}

So, either way you will execute setDefaultDbValue() which will update the state and trigger a rerender.

To prevent this loop you can wrap this snipped with useEffect() and use your text.length as a dependency:

useEffect(() => {
    if (!props.text) return; // wait until it is defined.
    if (props.text.length > 0) setDefaultDbValue(props.text[0]);
    else setDefaultDbValue("No Db Available");
}, [props.text?.length]);

There is no need to pass defaultValue as dependency to the useEffect hook. Or do I missunderstand your intention with this?

This aside: the better way to set default values is to do it in the useState(<DEFAULT_VALUE>) hook directly. Just the way you did it. Why is this not an option for you?

  • Related