i'm trying to implement search functionality but i don't want to call the api every time i type something . here is the code:
const [term, setTerm] = useState("");
const [result, setResult] = useState([]);
useEffect(() => {
const search = async () => {
const respond = await axios.get("https://en.wikipedia.org/w/api.php", {
params: {
action: "query",
list: "search",
origin: "*",
format: "json",
srsearch: term,
},
});
setResult(respond.data.query.search);
};
if (!result.length) {
if (term) {
search();
}
}
}, [term, result.length]);
you can use the setTimeout() function and clearTimeout in the cleanup function
and the useRef hook to get the previous state to call the API only once :
const [result, setResult] = useState([]);
const termUseRef = useRef();
useEffect(() => {
termUseRef.current = term
})
const prevTerm = termUseRef.current;
useEffect(() => {
const search = async () => {
const respond = await axios.get('https://en.wikipedia.org/w/api.php', {
params: {
action: 'query',
list: 'search',
origin: '*',
format: 'json',
srsearch: term,
},
});
setResult(respond.data.query.search);
};
if (!result.length) {
if (term) {
search();
}
} else if(term !== prevTerm) {
const debounceSearch = setTimeout(() => {
if (term) {
search();
}
}, 1200);
return () => {
clearTimeout(debounceSearch);
};
}
}, [term, result.length, prevTerm]);
Related
I want to revalidate the date when on change. This is what I tried:
const fetchData = async() => {
const {
data
} = await axios.get(`/api/admin/orders${criteria}`, {
params: {
name: debouncedValue,
},
});
return data;
};
const {
data,
error: err,
mutate,
} = useSWR(`/api/admin/orders${criteria}/${nameSearch}`, fetchData);
const handleChange = (e: React.ChangeEvent < HTMLInputElement > ) => {
mutate();
setNameSearch(e.target.value);
};
But the data is no revalidate, I have to use the onTabFocus revalidation.
I have a custom react hook fetching number of comments from an API that looks like this:
export async function useFetchNumberOfComments(articleId) {
const [numberOfComments, setNumbeOfComments] = useState(0);
useEffect(() => {
(async () => {
try {
const response = await axios.get(`https://example.com/${articleId}`, {
headers: {
"Content-Type": "application/json",
"X-API-KEY": "X",
Authorization: localStorage.getItem("access_token"),
},
});
const data = await response.data;
setNumbeOfComments(data);
} catch (err) {
console.log(err);
}
})();
}, []);
return numberOfComments;
}
And I want to use it in a Article component that looks like this:
import { useFetchNumberOfComments } from "../utils";
const SingleArticle = () => {
let { id } = useParams();
// Important state
const [numOfComments, setNumOfComments] = useState(0);
// Not important states
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
const [content, setContent] = useState("");
const [comments, setComments] = useState([]);
const [commentAuthor, setCommentAuthor] = useState("");
const [commentContent, setCommentContent] = useState("");
const [imageId, setImageId] = useState("");
const [imageUrl, setImageUrl] = useState("");
const [createdAt, setCreatedAt] = useState();
const postComment = async (e) => {
e.preventDefault();
const dataToSend = {
articleId: id,
author: commentAuthor,
content: commentContent,
};
try {
await axios.post(`https://example.com/comments`, dataToSend, {
headers: {
"Content-Type": "application/json",
"X-API-KEY": "X",
Authorization: localStorage.getItem("access_token"),
},
});
// Here, fetch the number of comments from my custom hook and update numOf Comments in this component
setCommentAuthor("");
setCommentContent("");
} catch (err) {
console.log(err);
}
};
return (
<>
<form onSubmit={postComment}>
// Here are some inputs, labels and a submit button
</form>
<h4 className={styles.h1}>Comments({numOfComments})</h4>
</>
);
};
export default SingleArticle;
Now, the problem is that I have no idea how to do the mentioned activity within the Article component: Once the form data(for comment) are sent, trigger the useFetchNumberOfComments custom hook and set the numOfComments state inside article component to the newly fetched data.
I think you'd be better served refactoring the useFetchNumberOfComments hook to return a fetch function and some fetch request meta data, i.e. loading and response and error states.
Example:
export function useFetchNumberOfComments() {
const [numberOfComments, setNumbeOfComments] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchArticleCommentCount = useCallback(async (articleId) => {
setLoading(true);
try {
const response = await axios.get(`https://example.com/${articleId}`, {
headers: {
"Content-Type": "application/json",
"X-API-KEY": "X",
Authorization: JSON.parse(localStorage.getItem("access_token")),
},
});
const data = await response.data;
setNumbeOfComments(data);
setError(null);
return data;
} catch (err) {
console.log(err);
setError(err);
} finally {
setLoading(false);
}
}, []);
return {
fetchArticleCommentCount,
numberOfComments,
loading,
error
};
};
...
import { useFetchNumberOfComments } from "../utils";
const SingleArticle = () => {
const { id } = useParams();
const {
fetchArticleCommentCount,
numberOfComments,
} = useFetchNumberOfComments();
// Important state
const [numOfComments, setNumOfComments] = useState(0);
// Not important states
...
const postComment = async (e) => {
e.preventDefault();
const dataToSend = {
articleId: id,
author: commentAuthor,
content: commentContent,
};
try {
await axios.post(`https://example.com/comments`, dataToSend, {
headers: {
"Content-Type": "application/json",
"X-API-KEY": "X",
Authorization: localStorage.getItem("access_token"),
},
});
// await returned comment count and update local state
const commentCount = await fetchArticleCommentCount(id);
setNumOfComments(commentCount);
// or use the updated numberOfComments value returned from hook
fetchArticleCommentCount(id);
// both are available, but you only need one or the other here
setCommentAuthor("");
setCommentContent("");
} catch (err) {
console.log(err);
}
};
return (
<>
<form onSubmit={postComment}>
// Here are some inputs, labels and a submit button
</form>
<h4 className={styles.h1}>Comments({numberOfComments})</h4>
</>
);
};
export default SingleArticle;
Hi so I'm trying to grab some json from an api and then populate a table, pretty simple stuff.
What's happening is that I can see the "tableData" state being updated as each new row comes in, I'm also logging every time "tableData" is updated, yet maybe .5 seconds after its all done my "tableData" is empty again (check console screenshots)
const [bigChartData, setbigChartData] = React.useState("data1");
const [tableData, setTableData] = React.useState([]);
const setBgChartData = (name) => {
setbigChartData(name);
};
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.forEach(item => {
setTableData(oldData => [...oldData, {
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
}]);
});
})
.catch(err => console.log(err));
setTimeout(function () {
console.log(tableData);
}, 3000);
}
useEffect(() => {
getData();
}, []);
useEffect(() => {
console.log("Table data updated:");
console.log(tableData);
}, [tableData]);
I think you should not iterate through each row inside getData() method instead try following code
const getData = () => {
axios.get("URL")
.then(res => {
const data = res.data.items.map(item => {
return{
data: [
{ text: item.title },
{ text: "asd" + item.url },
{ text: "some links..." }
]
};
});
setTableData(data)
}).catch(err => console.log(err));
}
or if you have already some data in tableData then
setTableData([...tableData, data])
This is my test code snippet but it throws an exception, TypeError: componentInstance.loadLoanApplication is not a function :
it('should render the SubmittedLoan', () => {
const loanData = {
data: {
id: 1,
};
const div = document.createElement('div');
const wrapper = mount(
<AppProviders>
<MemoryRouter initialEntries={['/review/153']}>
<SubmittedLoan
match={{ params: { loanId: 1, step: 1 } }}
history={{
location: { state: { from: 'register' } },
push() {},
}}
/>
</MemoryRouter>
</AppProviders>,
div,
);
const componentInstance = wrapper
.find(SubmittedLoan)
.children()
.first()
.children()
.first()
.instance();
const loanApplication = {
id: 1,
steps_data: [
{ slug: 'step_1', title: 'Step 1' },
{ slug: 'step_2', title: 'Step 2' },
],
status: ApiCaptiq.STATUS_SUBMITTED,
};
expect(wrapper.find(SubmittedLoan).length).toBe(1);
componentInstance.loadLoanApplication(1, 1);
componentInstance.onLoadLoanApplication(loanData);
componentInstance.onLoadFail();
componentInstance.setState({
formData: [{ item: 'value' }, { item2: 'value2' }],
activeStep: 1,
loanApplication,
});
componentInstance.handleSnackbarClose(new Event('click'), '');
componentInstance.setState({ activeStep: 3 });
});
Then my Component which uses memo is as follows :
export const SubmittedLoan = memo(() => {
const [loanApplication, setLoanApplication] = useState<LoanApplication | null>(null);
const [message, setMessage] = useState({
message: '',
open: false,
messageType: '',
});
const authContext = useContext(AuthContext);
const customerContext = useCustomerData();
const params = useParams();
const history = useHistory();
const classes = useStyles();
const { loanId } = params;
const onLoadFail = useCallback(() => {
setMessage({
message: 'Die verfügbaren Darlehensarten können nicht aufgelistet werden',
open: true,
messageType: 'error',
});
}, []);
const onLoadLoanApplication = useCallback(
(response: AxiosResponse) => {
setTemplateSettings(response, authContext);
if (
response.data.status === ApiCaptiq.STATUS_STARTING ||
response.data.status === ApiCaptiq.STATUS_IN_PROGRESS ||
response.data.status === ApiCaptiq.STATUS_PRE_WAITING
) {
history.push(`/view/${loanId}`);
} else {
setLoanApplication(response.data);
}
},
[loanId, authContext, history],
);
const loadLoanApplication = useCallback(
async (loan_id: number) => {
try {
const response = await request.get(`${ApiCaptiq.LOAN_APPLICATION_URL}${loan_id}/`);
const { fetchCustomerProfile } = customerContext;
await fetchCustomerProfile(response.data.customer_profile_id);
onLoadLoanApplication(response);
} catch (err) {
onLoadFail();
}
},
[customerContext, onLoadLoanApplication, onLoadFail],
);
...
What could be the possible reason for this
The functions you are defining inside the component, are not just available on the component instance. In fact, there is not way to call them. You can test only by mocking the fetch calls they are doing.
If you really need callable functions in your component (you should try to avoid these..), you could use this: https://reactjs.org/docs/hooks-reference.html#useimperativehandle
Perhaps better would be to extract this data loading logic elsewhere and test it separately.
I am trying to fetch and map an array with 350 object's elements. I decided to use Hook and useEffect, to re render my dataTable component since mapping is done. Unfortunately, the whole process takes enormous amount of time, and it makes page unresponsive. After 1-2 minutes, table shows up and after few seconds it disappears. After that page is still unresponsive. Could someone explain why it happens, and give me some workaround? I would be grateful.
Code below:
const Employees = (props) => {
const [developers, setDevelopers] = useState([]);
useEffect(() => {
fetchData();
});
const columns = [
{
name: "Emloyee",
selector: "name",
sortable: true,
},
{
name: "Team ",
selector: "team",
sortable: true,
},
{
name: "Email ",
selector: "email",
sortable: true,
},
];
const fetchData = () => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
};
const mapData = (jsonData) => {
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#mail.com",
};
setDevelopers((developers) => [...developers, newDeveloper]);
});
};
return <DataTable title="Employees" columns={columns} data={developers}/>;
};
useEffect without dependency array will run on every render, so in your case, you are stuck in an infinite loop which cause page to become unresponsive
solution:
const fetchData = useCallback(() => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
},[]);
const mapData = useCallback((jsonData) => {
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#kuehne-nagel.com",
};
setDevelopers((developers) => [...developers, newDeveloper]);
});
},[]);
useEffect(() => {
fetchData();
},[fetchData]); // pass dependency array here in useEffect
Thanks to #DrewReese and #SarthakAggarwal , I've got a solution:
const Employees = (props) => {
const [developers, setDevelopers] = useState([]);
const columns = [
{
name: "Emloyee",
selector: "name",
sortable: true,
},
{
name: "Team ",
selector: "team",
sortable: true,
},
{
name: "Email ",
selector: "email",
sortable: true,
},
];
const fetchData = useCallback(() => {
axios.get("http://localhost:3128/employees", {
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then((response) => {
mapData(response.data.developers);
console.log("I am here!");
})
.catch((e) => console.log(e));
}, []);
const mapData = (jsonData) => {
let table = [];
jsonData.forEach((x) => {
let newDeveloper = {
name: x.userId,
team: x.team,
email: x.userId + "#mail.com",
};
table = [...table,newDeveloper];
//setDevelopers((developers) => [...developers, newDeveloper]);
});
setDevelopers((developers) => table);
};
useEffect(() => {
fetchData();
}, [fetchData]);
return <DataTable title="Employees" columns={columns} data={developers}/>;
};
Thanks a lot !