like-gold
like-gold2w ago

I'm making a get api call every time a key is entered. How do I make it send only when onSubmit occu

const [searchField, setSearchField] = useState('')

const { data: members } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: searchField,
},
],
})

<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button type="submit" variant="contained" sx={{ mr: 2 }}>
검색하기
</Button>
<Button type="button" variant="contained" color="secondary" onClick={() => resetForm()}>
초기화
</Button>
</Box>
</CardContent>
const [searchField, setSearchField] = useState('')

const { data: members } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: searchField,
},
],
})

<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button type="submit" variant="contained" sx={{ mr: 2 }}>
검색하기
</Button>
<Button type="button" variant="contained" color="secondary" onClick={() => resetForm()}>
초기화
</Button>
</Box>
</CardContent>
26 Replies
kapa.ai
kapa.ai2w ago
Hi @florent. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Currently, your code is making an API call every time the searchField state changes because the useList hook automatically triggers a request when its dependencies (like filters) change. To make it send the request only when the form is submitted, you need to implement a separate state for the search term and only update it on form submission. Here's how you can modify your code:
import { useState } from "react";
import { useList } from "@refinedev/core";

// Component code
const YourComponent = () => {
const [searchField, setSearchField] = useState('');
const [submittedSearch, setSubmittedSearch] = useState('');

const { data: members } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch, // Use the submitted value instead of searchField
},
],
});

const handleSubmit = (e) => {
e.preventDefault();
setSubmittedSearch(searchField); // Update the submitted search value
};

const resetForm = () => {
setSearchField('');
setSubmittedSearch('');
};
return (
<form onSubmit={handleSubmit}>
<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button type="submit" variant="contained" sx={{ mr: 2 }}>
검색하기
</Button>
<Button type="button" variant="contained" color="secondary" onClick={() => resetForm()}>
초기화
</Button>
</Box>
</CardContent>
</form>
);
};
import { useState } from "react";
import { useList } from "@refinedev/core";

// Component code
const YourComponent = () => {
const [searchField, setSearchField] = useState('');
const [submittedSearch, setSubmittedSearch] = useState('');

const { data: members } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch, // Use the submitted value instead of searchField
},
],
});

const handleSubmit = (e) => {
e.preventDefault();
setSubmittedSearch(searchField); // Update the submitted search value
};

const resetForm = () => {
setSearchField('');
setSubmittedSearch('');
};
return (
<form onSubmit={handleSubmit}>
<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button type="submit" variant="contained" sx={{ mr: 2 }}>
검색하기
</Button>
<Button type="button" variant="contained" color="secondary" onClick={() => resetForm()}>
초기화
</Button>
</Box>
</CardContent>
</form>
);
};
This approach uses two separate states: 1. searchField - Updates as the user types in the input field 2. submittedSearch - Only updates when the form is submitted The useList hook will only make a new API call when submittedSearch changes, which happens only when the form is submitted Creating a React search bar and content filtering components. For better performance with large data sets, you might also consider implementing debouncing as mentioned in the Filter Optimization for Large Data Volumes section of the refine documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai How to make it load when submitting?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on your previous question about preventing API calls on every keystroke and only triggering them on form submission, I'll explain how to properly implement form submission in refine. To make your form load data only when submitting, you need to use refine's form handling capabilities. The useForm hook provides the functionality you need:
import { useForm } from "@refinedev/core";

const YourSearchComponent = () => {
const [searchField, setSearchField] = useState('');

const { onFinish, formLoading } = useForm();

const handleSubmit = (e) => {
e.preventDefault();
// This will trigger the data fetching only on submission
onFinish({
value: searchField
});
};

const resetForm = () => {
setSearchField('');
};
return (
<form onSubmit={handleSubmit}>
<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button
type="submit"
variant="contained"
sx={{ mr: 2 }}
disabled={formLoading}
>
{formLoading ? "로딩 중..." : "검색하기"}
</Button>
<Button
type="button"
variant="contained"
color="secondary"
onClick={() => resetForm()}
>
초기화
</Button>
</Box>
</CardContent>
</form>
);
};
import { useForm } from "@refinedev/core";

const YourSearchComponent = () => {
const [searchField, setSearchField] = useState('');

const { onFinish, formLoading } = useForm();

const handleSubmit = (e) => {
e.preventDefault();
// This will trigger the data fetching only on submission
onFinish({
value: searchField
});
};

const resetForm = () => {
setSearchField('');
};
return (
<form onSubmit={handleSubmit}>
<CardContent sx={{ pt: 0 }}>
<Box mb={2}>
<Typography variant="h6">검색어</Typography>
<Grid container spacing={2}>
<Grid item xs={12} sm={10}>
<TextField
value={searchField}
onChange={(e) => setSearchField(e.target.value)}
label={'검색'}
placeholder={'회원 휴대폰번호, 회원 이름, 회원 아이디'}
margin="normal"
fullWidth
autoFocus
size="small"
variant="outlined"
sx={{ mt: 0, height: 40 }}
/>
</Grid>
</Grid>
</Box>
<Box display="flex" justifyContent="flex-end" width="100%">
<Button
type="submit"
variant="contained"
sx={{ mr: 2 }}
disabled={formLoading}
>
{formLoading ? "로딩 중..." : "검색하기"}
</Button>
<Button
type="button"
variant="contained"
color="secondary"
onClick={() => resetForm()}
>
초기화
</Button>
</Box>
</CardContent>
</form>
);
};
The key points in this implementation: 1. Use the onFinish method from useForm to handle form submission Form Submission. 2. Create a handleSubmit function that prevents the default form behavior and calls onFinish with your search parameters. 3. Use the formLoading state to show loading indicators and disable the submit button while the request is in progress. If you need to modify the data before submission, you can do that within the handleSubmit function before calling onFinish as shown in the Modifying Data Before Submission documentation. This approach ensures that your API calls are only made when the form is submitted, rather than on every keystroke. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai oh I used useList. Can I use isLoading?
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})
const membersData: IMember[] = members?.data || []
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})
const membersData: IMember[] = members?.data || []
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, you can definitely use the isLoading state from the useList hook to handle loading states in your application. This is the correct way to destructure and use it:
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})
const membersData: IMember[] = members?.data || []
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})
const membersData: IMember[] = members?.data || []
The isLoading boolean will be true when the request is in progress and false when it completes. You can use this to show loading indicators in your UI:
if (isLoading) {
return <div>Loading...</div>;
}

// Render your data when loaded
return (
<ul>
{membersData.map((member) => (
<li key={member.id}>{/* member details */}</li>
))}
</ul>
);
if (isLoading) {
return <div>Loading...</div>;
}

// Render your data when loaded
return (
<ul>
{membersData.map((member) => (
<li key={member.id}>{/* member details */}</li>
))}
</ul>
);
This pattern is shown in the useList documentation and is a standard way to handle loading states in refine applications. The isLoading state is part of the underlying react-query functionality that refine leverages, as mentioned in the Chakra UI integration documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai Error: No QueryClient set, use QueryClientProvider to set one
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})

<Box sx={{ marginTop: 2, display: 'flex', flexDirection: 'column' }}>
<Show isLoading={isLoading}></Show>
<DataGrid
rows={membersData}
columns={[
{ field: 'id', headerName: 'ID', width: 70 },
{ field: 'member_name', headerName: '주문자 이름', width: 130 },
{ field: 'member_id', headerName: '아이디', width: 130 },
{ field: 'member_phone', headerName: '휴대폰번호', width: 150 },
{ field: 'membership_grade', headerName: '멤버십 등급', width: 130 },
{ field: 'subscription_status', headerName: '구독 상태', width: 130 },
{
field: 'change_status',
headerName: '등급 변경',
width: 150,
renderCell: (params) => (
<Button variant="contained" color="primary" onClick={() => handleRowClick(params)}>
상태 변경
</Button>
),
},
]}
checkboxSelection
/>
const { data: members, isLoading } = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})

<Box sx={{ marginTop: 2, display: 'flex', flexDirection: 'column' }}>
<Show isLoading={isLoading}></Show>
<DataGrid
rows={membersData}
columns={[
{ field: 'id', headerName: 'ID', width: 70 },
{ field: 'member_name', headerName: '주문자 이름', width: 130 },
{ field: 'member_id', headerName: '아이디', width: 130 },
{ field: 'member_phone', headerName: '휴대폰번호', width: 150 },
{ field: 'membership_grade', headerName: '멤버십 등급', width: 130 },
{ field: 'subscription_status', headerName: '구독 상태', width: 130 },
{
field: 'change_status',
headerName: '등급 변경',
width: 150,
renderCell: (params) => (
<Button variant="contained" color="primary" onClick={() => handleRowClick(params)}>
상태 변경
</Button>
),
},
]}
checkboxSelection
/>
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The error "No QueryClient set, use QueryClientProvider to set one" indicates that your application is trying to use React Query hooks (which refine's useList hook uses under the hood) without having a proper QueryClient provider set up. This is a common issue that occurs when the React Query context is not properly initialized. Based on the Github issue #5014 and Github issue #5499 in the knowledge sources, here's how to fix it: Solution: You need to ensure that your application is wrapped with a QueryClientProvider. In refine, this is typically handled by the <Refine /> component, but sometimes there can be issues with the component hierarchy. 1. Make sure your <Refine /> component is properly set up in your application's root:
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";
// other imports

const App = () => (
<Refine
dataProvider={dataProvider("<https://api.fake-rest.refine.dev">)}
// other props
>
{/* Your app components */}
</Refine>
);
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";
// other imports

const App = () => (
<Refine
dataProvider={dataProvider("<https://api.fake-rest.refine.dev">)}
// other props
>
{/* Your app components */}
</Refine>
);
2. If you're using Next.js, ensure that your page components are properly wrapped within the Refine context. The issue might be that your components are being rendered outside of the Refine context. 3. If you're still facing issues, you can explicitly set up a QueryClient in your application:
import { QueryClient } from "@tanstack/react-query";
import { Refine } from "@refinedev/core";

// Create a client
const queryClient = new QueryClient();

const App = () => (
<Refine
options={{
reactQuery: {
clientConfig: queryClient,
},
}}
// other props
>
{/* Your app components */}
</Refine>
);
import { QueryClient } from "@tanstack/react-query";
import { Refine } from "@refinedev/core";

// Create a client
const queryClient = new QueryClient();

const App = () => (
<Refine
options={{
reactQuery: {
clientConfig: queryClient,
},
}}
// other props
>
{/* Your app components */}
</Refine>
);
This approach is documented in the Refine component documentation. Note that refine is currently compatible with TanStack Query v4, so make sure you're using the correct version. If you're using v5, you might encounter compatibility issues as mentioned in Github issue #5499. For your specific code with DataGrid, once the QueryClient issue is resolved, your implementation looks correct. The isLoading state from useList can be used to show loading indicators while data is being fetched. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai const { data: members, isLoading } = useList<IMember>({ resource: 'membership/subscribers', filters: [ { field: 'value', operator: 'eq', value: submittedSearch, }, ], }) isLoading all ways "False" return
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on your issue with isLoading always returning false when using the useList hook, this is actually a known behavior in refine. The isLoading property from useList only indicates the initial loading state when data is being fetched for the first time. It doesn't reflect subsequent loading states when data is being refetched, such as when you change the submittedSearch value after the initial load. According to the GitHub issue #5090, this is expected behavior. To properly track loading states during refetching (which happens when your filters change), you should use isFetching or isRefetching instead:
const {
data: members,
isLoading,
isFetching,
isRefetching
} = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})

// Use a combined loading state
const isLoadingData = isLoading || isFetching || isRefetching;
const {
data: members,
isLoading,
isFetching,
isRefetching
} = useList<IMember>({
resource: 'membership/subscribers',
filters: [
{
field: 'value',
operator: 'eq',
value: submittedSearch,
},
],
})

// Use a combined loading state
const isLoadingData = isLoading || isFetching || isRefetching;
As mentioned in the GitHub issue, you can create a combined loading state variable like isLoadingData shown above to properly handle all loading scenarios. This is because under the hood, useList is an extended version of TanStack Query's useQuery hook, as mentioned in the React Query and Frameworks documentation. In TanStack Query, isLoading is only true during the initial load, while isFetching is true whenever a request is in flight. When you use this combined loading state with your UI components, you'll get the expected loading behavior:
<Show isLoading={isLoading || isFetching || isRefetching}>
{/* Your content */}
</Show>
<Show isLoading={isLoading || isFetching || isRefetching}>
{/* Your content */}
</Show>
This approach will ensure that loading indicators are displayed correctly during all data fetching operations. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai
const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()
if (result.code === 400) {
enqueueSnackbar(result.message, { variant: 'error', anchorOrigin: { vertical: 'top', horizontal: 'right' } })
} else {
enqueueSnackbar(result.message, {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
})
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
})
}
} catch (error) {
console.error('Error changing membership:', error)
}
}
handleClose()
}
const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()
if (result.code === 400) {
enqueueSnackbar(result.message, { variant: 'error', anchorOrigin: { vertical: 'top', horizontal: 'right' } })
} else {
enqueueSnackbar(result.message, {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
})
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
})
}
} catch (error) {
console.error('Error changing membership:', error)
}
}
handleClose()
}
fetch 되는 동안에 isLoading을 하게 하려면?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'll help you implement loading state during your fetch operation. Based on your question in Korean, you're asking how to show a loading state while the fetch operation is in progress. In refine, you can manage loading states during custom fetch operations by using React's useState hook. Here's how you can modify your code:
const [isLoading, setIsLoading] = useState(false);

const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
setIsLoading(true); // Set loading to true when starting the request
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()
if (result.code === 400) {
enqueueSnackbar(result.message, { variant: 'error', anchorOrigin: { vertical: 'top', horizontal: 'right' } })
} else {
enqueueSnackbar(result.message, {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
})
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
})
}
} catch (error) {
console.error('Error changing membership:', error)
} finally {
setIsLoading(false); // Set loading to false when request completes (success or error)
}
}
handleClose()
}
const [isLoading, setIsLoading] = useState(false);

const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
setIsLoading(true); // Set loading to true when starting the request
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()
if (result.code === 400) {
enqueueSnackbar(result.message, { variant: 'error', anchorOrigin: { vertical: 'top', horizontal: 'right' } })
} else {
enqueueSnackbar(result.message, {
variant: 'success',
anchorOrigin: { vertical: 'top', horizontal: 'right' },
})
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
})
}
} catch (error) {
console.error('Error changing membership:', error)
} finally {
setIsLoading(false); // Set loading to false when request completes (success or error)
}
}
handleClose()
}
Then you can use this isLoading state to show a loading indicator in your UI. For example, if you're using a button to trigger this action:
<Button
onClick={handleSave}
disabled={isLoading}
>
{isLoading ? "처리 중..." : "저장"}
</Button>
<Button
onClick={handleSave}
disabled={isLoading}
>
{isLoading ? "처리 중..." : "저장"}
</Button>
If you want to show a loading overlay, you can use refine's UI components like <Show isLoading={isLoading}> as shown in the Chakra UI documentation or similar components from other UI frameworks that refine supports. This pattern follows the container and presentation pattern described in the React Design Patterns blog post, where you separate the loading state logic from the UI presentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai
const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()

<DialogActions>
<Button onClick={handleClose} color="primary">
취소
</Button>
<Button onClick={handleSave} color="primary">
저장
</Button>
</DialogActions>
const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
})

const result = await response.json()

<DialogActions>
<Button onClick={handleClose} color="primary">
취소
</Button>
<Button onClick={handleSave} color="primary">
저장
</Button>
</DialogActions>
How can I change this to refine code?
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I can help you convert your custom fetch code to use refine's data hooks for better integration with the framework. Refine provides hooks like useCreate or useUpdate that can replace your manual fetch implementation. Here's how you can refactor your code using refine's hooks:
import { useCreate } from "@refinedev/core";
import { SaveButton } from "@refinedev/mui"; // or the UI package you're using

// Inside your component
const { mutate, isLoading } = useCreate();

const handleSave = () => {
if (selectedMember && newGroupNo !== null) {
mutate(
{
resource: "membership/change",
values: {
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
},
},
{
onSuccess: (data) => {
// Handle success
enqueueSnackbar(data.message || "Successfully updated", {
variant: "success",
anchorOrigin: { vertical: "top", horizontal: "right" },
});
// Invalidate the list to refresh data
invalidate({
resource: "membership/subscribers",
invalidates: ["list"],
});
handleClose();
},
onError: (error) => {
// Handle error
enqueueSnackbar(error?.message || "An error occurred", {
variant: "error",
anchorOrigin: { vertical: "top", horizontal: "right" },
});
},
}
);
}
};

// In your JSX
<DialogActions>
<Button onClick={handleClose} color="primary">
취소
</Button>
<SaveButton
onClick={handleSave}
loading={isLoading}
color="primary"
>
저장
</SaveButton>
</DialogActions>
import { useCreate } from "@refinedev/core";
import { SaveButton } from "@refinedev/mui"; // or the UI package you're using

// Inside your component
const { mutate, isLoading } = useCreate();

const handleSave = () => {
if (selectedMember && newGroupNo !== null) {
mutate(
{
resource: "membership/change",
values: {
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
},
},
{
onSuccess: (data) => {
// Handle success
enqueueSnackbar(data.message || "Successfully updated", {
variant: "success",
anchorOrigin: { vertical: "top", horizontal: "right" },
});
// Invalidate the list to refresh data
invalidate({
resource: "membership/subscribers",
invalidates: ["list"],
});
handleClose();
},
onError: (error) => {
// Handle error
enqueueSnackbar(error?.message || "An error occurred", {
variant: "error",
anchorOrigin: { vertical: "top", horizontal: "right" },
});
},
}
);
}
};

// In your JSX
<DialogActions>
<Button onClick={handleClose} color="primary">
취소
</Button>
<SaveButton
onClick={handleSave}
loading={isLoading}
color="primary"
>
저장
</SaveButton>
</DialogActions>
This implementation uses refine's useCreate hook which handles the API request, loading state, and error handling for you. The SaveButton component from refine's UI packages (like @refinedev/mui, @refinedev/antd, etc.) can be used to show loading states automatically SaveButton documentation. If you need more control over the form submission, you can also use the useForm hook which provides form handling capabilities including validation and submission:
import { useForm } from "@refinedev/core"; // or from your UI package

const { onFinish, formLoading } = useForm({
action: "create",
resource: "membership/change",
onMutationSuccess: () => {
// Handle success
invalidate({
resource: "membership/subscribers",
invalidates: ["list"],
});
handleClose();
}
});

const handleSave = () => {
if (selectedMember && newGroupNo !== null) {
onFinish({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
});
}
};

// Then in your JSX
<SaveButton
onClick={handleSave}
loading={formLoading}
color="primary"
>
저장
</SaveButton>
import { useForm } from "@refinedev/core"; // or from your UI package

const { onFinish, formLoading } = useForm({
action: "create",
resource: "membership/change",
onMutationSuccess: () => {
// Handle success
invalidate({
resource: "membership/subscribers",
invalidates: ["list"],
});
handleClose();
}
});

const handleSave = () => {
if (selectedMember && newGroupNo !== null) {
onFinish({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
});
}
};

// Then in your JSX
<SaveButton
onClick={handleSave}
loading={formLoading}
color="primary"
>
저장
</SaveButton>
This approach leverages refine's form handling capabilities as described in the Forms documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai that's good. but notification not show this message
server api response
return {
type: 'success',
message: '등급 변경 완료',
description: '등급 변경 완료',
key: 'notification-key',
};
server api response
return {
type: 'success',
message: '등급 변경 완료',
description: '등급 변경 완료',
key: 'notification-key',
};
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I see the issue. Your server is returning a notification object, but it's not being displayed automatically. In refine, you need to use the notification provider to show these messages. Based on the knowledge sources, refine provides a notification system that requires proper setup to display messages. Here's how you can show the notification from your server response:
import { useNotification } from "@refinedev/core";

// Inside your component
const { open } = useNotification();

const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
});

const result = await response.json();

// Display notification using refine's notification provider
open?.(result); // Pass the notification object directly

// Invalidate the list to refresh data
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
});
} catch (error) {
console.error('Error changing membership:', error);
}
handleClose();
}
};
import { useNotification } from "@refinedev/core";

// Inside your component
const { open } = useNotification();

const handleSave = async () => {
if (selectedMember && newGroupNo !== null) {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/membership/change`, {
headers: {
'Content-Type': 'application/json',
'ngrok-skip-browser-warning': '69420',
},
method: 'POST',
body: JSON.stringify({
member_id: selectedMember.member_id,
change_group_no: newGroupNo,
}),
});

const result = await response.json();

// Display notification using refine's notification provider
open?.(result); // Pass the notification object directly

// Invalidate the list to refresh data
invalidate({
resource: 'membership/subscribers',
invalidates: ['list'],
});
} catch (error) {
console.error('Error changing membership:', error);
}
handleClose();
}
};
The key here is using the useNotification hook from refine, which provides the open method to display notifications Using React Hot Toast as a Notification Provider for CRUD apps. Make sure you have a notification provider set up in your Refine component:
import { Refine } from "@refinedev/core";
import { useNotificationProvider } from "@refinedev/your-ui-framework"; // e.g., @refinedev/antd, @refinedev/mantine, etc.

<Refine
// ...other props
notificationProvider={useNotificationProvider}
>
{/* Your app components */}
</Refine>
import { Refine } from "@refinedev/core";
import { useNotificationProvider } from "@refinedev/your-ui-framework"; // e.g., @refinedev/antd, @refinedev/mantine, etc.

<Refine
// ...other props
notificationProvider={useNotificationProvider}
>
{/* Your app components */}
</Refine>
If you're using a specific UI framework like Mantine or Chakra UI, you'll need to use the corresponding notification provider as shown in the Built-in Notification Providers documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
like-gold
like-goldOP2w ago
@kapa.ai When server api response HTTP 500. How can I catch error?
const { onFinish, formLoading } = useForm({
action: 'create',
resource: 'membership/change',
successNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
errorNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
onMutationSuccess: () => {
handleClose()
},
})
const { onFinish, formLoading } = useForm({
action: 'create',
resource: 'membership/change',
successNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
errorNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
onMutationSuccess: () => {
handleClose()
},
})
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I can help you with handling HTTP 500 errors in refine. When your server API returns a 500 error, you can catch and handle it using the errorNotification and onMutationError properties in the useForm hook. Based on the documentation, here's how you can properly catch and handle server errors:
const { onFinish, formLoading } = useForm({
action: 'create',
resource: 'membership/change',
successNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
errorNotification: (error, values, resource) => {
// This will be called when the server returns an error (including 500)
return {
message: `Error: ${error?.message || 'Something went wrong'}`,
description: error?.data?.message || `Status code: ${error?.statusCode || 500}`,
type: "error",
};
},
onMutationError: (error, variables, context) => {
// You can perform additional actions when an error occurs
console.error("Mutation error:", error);
// You can also access the status code
if (error?.statusCode === 500) {
console.log("Server error occurred");
}
},
onMutationSuccess: () => {
handleClose()
},
})
const { onFinish, formLoading } = useForm({
action: 'create',
resource: 'membership/change',
successNotification: (data) => {
return {
message: data?.data?.message,
type: data?.data?.type,
}
},
errorNotification: (error, values, resource) => {
// This will be called when the server returns an error (including 500)
return {
message: `Error: ${error?.message || 'Something went wrong'}`,
description: error?.data?.message || `Status code: ${error?.statusCode || 500}`,
type: "error",
};
},
onMutationError: (error, variables, context) => {
// You can perform additional actions when an error occurs
console.error("Mutation error:", error);
// You can also access the status code
if (error?.statusCode === 500) {
console.log("Server error occurred");
}
},
onMutationSuccess: () => {
handleClose()
},
})
The errorNotification property allows you to customize the error notification that appears when the request fails, as described in the useForm documentation. The function receives the error object which contains details about the error, including the status code. The onMutationError callback gives you more control to handle specific error cases or perform additional actions when an error occurs. If you need to handle server-side validation errors specifically, refine also supports that through the HttpError interface with an errors field that can propagate validation errors to form fields, as mentioned in the Server Side Validation documentation. Remember that for this to work properly, you need to have a NotificationProvider set up in your refine application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@1184365192482652173> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?