S
SolidJS2y 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
TurOP2y 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
Khyonn2y ago
I guess this is because you read and write formValues multiple times
<Alterion.Dev>
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
Khyonn2y 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>
Perhaps what you might want to do is a derived signal instead? https://www.solidjs.com/tutorial/introduction_derived
Tur
TurOP2y 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
Khyonn2y 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
TurOP2y 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>
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