i tried to use this code
useFocusEffect(
useCallback(() => {
const getLocation = async() => {
await getPermissionsAsync()
const subscribe = await watchPositionAsync({
accuracy:Accuracy.BestForNavigation,
timeInterval:5000
},(location) => {console.log(1)})
return () => subscribe.remove()
}
return getLocation()
}, [])
);
but i get this error
An effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote 'useFocusEffect(async () => ...)' or returned a Promise. Instead, write the
async function inside your effect and call it immediately:
useFocusEffect(
React.useCallback() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId])
};
TL;DR
useFocusEffect(
useCallback(() => {
let subcription;
const getLocation = async() => {
await getPermissionsAsync()
subcription = await watchPositionAsync({
accuracy:Accuracy.BestForNavigation,
timeInterval:5000
}, (location) => {console.log(1)})
}
getLocation();
return () => { subscription && subscription.remove(); }
}, [])
);
The error actually says what needs to occur. that is the return value must be a simple function used for cleanup.
The subscription is obtained as part of an async call in the getLocation function, but for it to be used it must be pulled out and have it set asynchronously.
Then the cleanup function just needs to check if it is already set and if it is invoke the remove method.
Related
I want to test my code using JEST, but I'm having some issues. I want to check, if restart() function has been called.
My code works like this, it's waiting for the data, and if there's no data it's calling the same function again. Basically something like a loop.
myCode.js file:
module.exports = {
getSomething: async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("");
}, 1000);
});
},
doSomething: async () => {
const data = await module.exports.getSomething();
if (!data) {
return module.exports.restart();
}
return data;
},
restart: async () => {
return module.exports.doSomething();
}
};
myCode.test.js file:
const myCode = require("./exampleCode")
describe("test", () => {
test("Is it doing something more than once?", async () => {
const restartSpy = jest.spyOn(myCode, 'restart');
myCode.doSomething()
expect(restartSpy).toHaveBeenCalledTimes(1);
})
})
My problem is that expect(restartSpy).toHaveBeenCalledTimes(1); is returning false.
The question is - what I'm doing wrong? Is there a way to test this code?
The main problem here is the lack of await before myCode.doSomething(). All your functions are asynchronous, so you need to wait for them to finish before checking the spy:
await myCode.doSomething();
Another issue is the fact that it's an infinite recursion loop - jest will timeout after 5000ms (by default) if you won't modify the code that is calling restart, for example:
doSomething: async (restartCounter = 0) => {
const data = await module.exports.getSomething();
if (!data && ++restartCounter < 2) {
return module.exports.restart(restartCounter);
}
return data;
},
restart: async (restartCounter) => {
return module.exports.doSomething(restartCounter);
}
Actually, I've found a workaround.
describe("test", () => {
test("Is it doing something more than once?", async () => {
myCode.restart = jest.fn()
const restartSpy = jest.spyOn(myCode, 'restart');
await myCode.doSomething()
expect(restartSpy).toHaveBeenCalledTimes(1);
})
})
I'm overwriting restart() function. So now, I'm able to add await to doSomething() function and it will no longer be inifinite loop. Now I can check if the restart function has been called
I'm trying to debounce() an Observable with pipe() and chaining .subscribe() but for some reason the function in the subscribe is still being called over a dozen times in one go.
What I'm trying to do is pipe the withChangesForTables and debounce the sync call because I want it to be called only when a whole batch of changes have been made. So I created a provider for the sync and wrapped it around my RootNavigator
withChangesForTables on WatermelonDB source code
const SyncContext = createContext();
function useSync() {
return useContext(SyncContext);
}
function SyncProvider({children}) {
const [isSyncing, setIsSyncing] = useState(false);
const [hasUnsynced, setHasUnsynced] = useState(false);
async function checkUnsyncedChanges() {
const hasChanges = await hasUnsyncedChanges({
database
});
setHasUnsynced(hasChanges);
}
async function sync() {
await checkUnsyncedChanges();
if (!isSyncing && hasUnsynced) {
setIsSyncing(true);
await synchronizeWithServer();
setIsSyncing(false);
}
}
database.withChangesForTables([
'table_name',
'table_name2'
]).pipe(
skip(1),
// ignore records simply becoming `synced`
filter(changes => !changes.every(change => change.record.syncStatus === 'synced')),
// debounce to avoid syncing in the middle of related actions - I put 100000 to test only
debounceTime(100000),
).subscribe({
//calls API endpoint to sync local DB with server
next: () => sync(),
error: e => console.log(e)
});
const value = {
isSyncing,
hasUnsynced,
checkUnsyncedChanges,
sync
};
return (
<SyncContext.Provider value={value}>
{children}
</SyncContext.Provider>
);
}
I had to move withChangesForTables into a useEffect and retrun it in order to unsubcribe which seems to have resolved the issue. The code now looks something like this:
useEffect(() => {
return database.withChangesForTables([
'table_name',
'table_name2'
]).pipe(
skip(1),
filter(changes => !changes.every(change => change.record.syncStatus === 'synced')),
debounceTime(500),
).subscribe({
next: () => sync(),
error: e => console.log(e)
});
}, [])
Below is the code to fetch some data
useEffect(async (id) => {
// const data = await redditFetch(id);
async function redditFetch(id){
try {
const {data:{data}} = await axios.get(`https://www.reddit.com/search.json?q=${id}&limit=50`);
const {children} = data;
return children.map(data=> data.data);
} catch (error) {
console.log(error);
}
}
const data = redditFetch(id);
setData(data)
},[setData])
console.log(Data);
And this is the promise returned:
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(50)
What I want is the Array inside the PromiseResult.
I've also tried another away using async await like this which returns a proper array.
const PostPage = () => {
const {id} = useParams();
const [Data, setData] = useState([])
useEffect(async (id) => {
// const data = await redditFetch(id);
async function redditFetch(id){
try {
const {data:{data}} = await axios.get(`https://www.reddit.com/search.json?q=${id}&limit=50`);
const {children} = data;
return children.map(data=> data.data);
} catch (error) {
console.log(error);
}
}
const data = await redditFetch(id);
setData(data)
},[setData])
console.log(Data);
But with this comes a bloody warning that I can't get rid of:
index.js:1 Warning: An effect function must not return anything
besides a function, which is used for clean-up.
It looks like you wrote useEffect(async () => ...) or returned a
Promise. Instead, write the async function inside your effect and call
it immediately:
useEffect(() => { async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ... } fetchData(); }, [someId]); // Or [] if effect doesn't need props or state
So, my dear coders, tell me what to do???
I did a sample demo with the post code:
CODESANDBOX: https://codesandbox.io/s/flamboyant-nobel-p87mt9?file=/src/App.js:257-265
In your code const data = redditFetch(id); the async functions will return a Promise. You have to use await, but React will warn you to not transform the useEffect callback in async function
You can remove the API function of your useEffect.
Don't put the setDate in your useEffect dependency array. In this case, you can leave this empty to run just one time. Also, don't put the date because will re-render in an infinite loop. If you need some conditional just remove the dependency array and put some conditional to run setDate.
I had to mock the id in the query parameter to be possible to work with API
CODE:
import axios from "axios";
import { useEffect, useState } from "react";
import "./styles.css";
async function redditFetch() {
try {
const {
data: { data }
} = await axios.get(`https://www.reddit.com/search.json?q=01&limit=50`);
const { children } = data;
return children.map((data) => data.data);
} catch (error) {
console.log(error);
}
}
export default function App() {
const [data, setData] = useState([]);
const Data = data.map((redditContent) => {
return (
<div key={redditContent.name}>
<h2>{redditContent.title}</h2>
<hr />
</div>
);
});
useEffect(() => {
(async function () {
const redditResponse = await redditFetch();
console.log(redditResponse);
setData(redditResponse);
})();
}, []);
return <div className="App">{Data}</div>;
}
ALTERNATIVE: CLEANER APPROACH (No anonymous function)
If you don't like of anonymous function approach, you can use this one:
async function updateRedditPosts() {
const redditResponse = await redditFetch();
console.log(redditResponse);
setData(redditResponse);
}
useEffect(() => {
updateRedditPosts();
}, []);
Anything return from useEffect supposed to be clean up while component unmount. Better you may invoke setData function there instead of return.
On the other hand, you may use immediate invoke function (IIF) then you do not need to resolve the promise that return from redditFetch function.
In my code, I have loadData() and useFilter(). I tried to run loadData() first as productsData need to be set for useFilter() to process productsData. However, I tried .then() but it still gives me undefined when I console log useFilter() How do I fix this?
const [productsData, setProductsData] = useState(null);
const loadData = async () => {
const { data } = await getAxios().get('/market/list').then(response => response.data)
setProductsData(data)
}
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
loadData().then(()=>useFilter());
},[])
Changing state asynchronous. So using state immediately after changing that may not be updated one.
You can use useEffect to call your function after state changed.
You can do this:
const [productsData, setProductsData] = useState(null);
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
const loadData = async () => {
const { data } = await getAxios().get('/market/list').then(response => response.data)
setProductsData(data)
}
loadData()
},[])
useEffect(()=>{
useFilter();
},[productsData])
Hello just by making a function async doesn't make it return a promise in order to use .then on function calls you must make sure they return promises
So one implementation you could do is :
const [productsData, setProductsData] = useState(null);
const loadData = () => {
return getAxios().get('/market/list').then(response => response.data)
}
const useFilter = () => {
return productsData.map(data => (...))
//filters productsData
};
useEffect(()=>{
loadData().then((data)=>{
setProductsData(data);
useFilter();
}
);
},[])
In the above implementation I return the promise that axios returns you can also return your own promise by creating a promise using new Promise() constructor
Do let me know if you face any issues
I'm trying to create the simplest React Hook useEffect call I can and hoping not to have to create a function call inside useEffect. My code is below and it requires me to call fetchData() to make it work. Is there a simpler version of this?
Maybe with a strategically placed async keyword or something like that?
useEffect(() => {
const fetchData = async () => {
let result = await axios.get('http://localhost:4000/');
console.log(result.data.length);
};
fetchData();
}, []);
What you can do is make it an IIFE:
useEffect(() => {
const promise = (async () => {
let result = await axios.get('http://localhost:4000/');
console.log(result.data.length);
})();
return (() => {
promise.then(() => cleanup());
});
}, []);
Or use plain promises:
useEffect(() => {
const promise = axios.get('http://localhost:4000/').then((result) => {
console.log(result.data.length);
});
return (() => {
promise.then(() => cleanup());
})
}, []);
But that is about as much as you can do since useEffect cannot be async directly. From ESLint:
Effect callbacks are synchronous to prevent race conditions.