S
SolidJS16mo ago
Tur

Maximum call stack size exceeded while using createEffect

Hello! I'm trying to fetch data from API endpoint and fill in the store values using createEffect
function AdminUserChange() {
const parentUrl = '/admin/users';
const params = useParams();
const navigate = useNavigate();
const thisLocation = `/admin/users/${params.itemID}/change`;
const [formValues, setFormValues] = createStore({
phone_number: { value: '', validators: [isRequired, phoneFormat], errors: '', formatter: removeExtraSymbols },
email: { value: '', validators: [isRequired, emailValidator], errors: '' },
first_name: { value: '', validators: [], errors: '' },
last_name: { value: '', validators: [], errors: '' },
patronymic: { value: '', validators: [], errors: '' },
is_active: { value: null, validators: [], errors: '' },
is_staff: { value: null, validators: [], errors: '' },
is_superuser: { value: null, validators: [], errors: '' },
});

const url = `${import.meta.env.HIN_SERVER_API_ROOT}/users/${params.itemID}`;
const [userData] = createResource(params.itemID, async () => await fetchFromAPI(url));

createEffect(() => {
if (userData()) {
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: userData()[key] } });
});
} else if (userData.error) {
if (userData.error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});

.... and so on
function AdminUserChange() {
const parentUrl = '/admin/users';
const params = useParams();
const navigate = useNavigate();
const thisLocation = `/admin/users/${params.itemID}/change`;
const [formValues, setFormValues] = createStore({
phone_number: { value: '', validators: [isRequired, phoneFormat], errors: '', formatter: removeExtraSymbols },
email: { value: '', validators: [isRequired, emailValidator], errors: '' },
first_name: { value: '', validators: [], errors: '' },
last_name: { value: '', validators: [], errors: '' },
patronymic: { value: '', validators: [], errors: '' },
is_active: { value: null, validators: [], errors: '' },
is_staff: { value: null, validators: [], errors: '' },
is_superuser: { value: null, validators: [], errors: '' },
});

const url = `${import.meta.env.HIN_SERVER_API_ROOT}/users/${params.itemID}`;
const [userData] = createResource(params.itemID, async () => await fetchFromAPI(url));

createEffect(() => {
if (userData()) {
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: userData()[key] } });
});
} else if (userData.error) {
if (userData.error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});

.... and so on
But I get an exception. The exception is Uncaught (in promise) RangeError: Maximum call stack size exceeded. I can't get why it happens.
9 Replies
Tur
Tur16mo ago
I used to use other approach to solve this task and it works well but it is not idiomatic:
nMount(async () => {
const url = `${import.meta.env.HIN_SERVER_API_ROOT}/users/${params.itemID}`;
try {
const responseData = await fetchFromAPI(url);
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: responseData[key] } });
});
} catch (error) {
if (error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});
nMount(async () => {
const url = `${import.meta.env.HIN_SERVER_API_ROOT}/users/${params.itemID}`;
try {
const responseData = await fetchFromAPI(url);
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: responseData[key] } });
});
} catch (error) {
if (error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});
Why does using createEffect here lead to infinite cycle?
Khyonn
Khyonn16mo ago
I guess this is because you read and write formValues multiple times
<Alterion.Dev>
<Alterion.Dev>16mo ago
well, if you setFormValues, you're updating that state, and it triggers the createEffect again. which of course updates form values. which triggers the createEffect again.
Khyonn
Khyonn16mo ago
since you read formValues in a createEffect, il will trigger the effect if it changes... but you change its value in the effect
<Alterion.Dev>
<Alterion.Dev>16mo ago
Perhaps what you might want to do is a derived signal instead? https://www.solidjs.com/tutorial/introduction_derived
Tur
Tur16mo ago
Thanks to @lexlohr this code works
createEffect((done) => {
if (!done && !userData.loading) {
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: userData()[key] } });
});
return true;
} else if (userData.error) {
if (userData.error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});
createEffect((done) => {
if (!done && !userData.loading) {
Object.keys(formValues).forEach((key) => {
setFormValues({ ...formValues, [key]: { ...formValues[key], value: userData()[key] } });
});
return true;
} else if (userData.error) {
if (userData.error.response.status === 401) {
navigate(`/auth/login?next=${thisLocation}`);
}
}
});
The thing is in that create effect expects getting true from inner func to stop triggering
Khyonn
Khyonn16mo ago
... well, it's an other way to do this ... but you should probably check out at derived signals like @eslachance said. Because, I don't think it's a good practice to edit an API data directly
Tur
Tur16mo ago
Thanks for the answer! But why does changing the store trigger createResource when it must be triggered by changing of params.itemID?
<Alterion.Dev>
<Alterion.Dev>16mo ago
I'm not seeing a dependency to params in your createEffect though? not sure about the createResource
Want results from more Discord servers?
Add your server