I'm new to React Native code building.
Below is my React Native code to get data from Firebase.
const page_one = () => {
const [isLoading, setIsLoading] = useState(true)
const [placeList, setPlaceList] = useState([])
const [message, setMessage] = useState(false)
const db = firebase.firestore().collection('places')
const onLoad = async () => {
const place_ref = await firebase.firestore().collection('places').get()
if (place_ref.empty) {
setMessage(true)
return
}
const places = []
try {
place_ref.forEach(doc => {
const entity = doc.data()
entity.id = doc.id
places.push(entity)
})
setPlaceList(places)
setMessage(false)
setIsLoading(false)
return
} catch (error) {
console.log("Error:\n", error.message)
return
}
}
}
useEffect(() => {
onLoad()
console.log('place List')
}, [isLoading])
return (<View></View>)
}
I need to refresh the current component every time I render, to get newly added data from firebase. How to make possible this.
As of now component is not loading when I rendering the component 2nd time. it fetches the old data only, not loading the latest data still I refreshing the whole application.
I need to fetch the latest data whenever I render the component.
I tried with below useEffect hook:
useEffect(() => {
onLoad()
console.log('place List')
}, [isLoading, placeList])
But it calls the firebase request n number of times till I existing the current component.
I want to call the firebase request only once when ever I visiting the current component.
please help me..
As far as I understand you need to refresh whenever this component gets focused
So for that, write like this
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
onLoad() // Gets fired whenever this screen is in focus
});
return unsubscribe;
}, [navigation]);
Also don't forget to destructure the props to get the navigation prop
Like this
const page_one = ({ navigation }) => {
...Code Inside
}
Related
I have been building a Laravel and React app and I had encountered something very embarassing.
The state variable context value is not changing with setState function. The code is following.
const ApiProvider = ({ children }) => {
const [data, setData] = React.useState({})
const [loading, setLoading] = React.useState(true)
const [repNumbers, setRepNumbers] = React.useState({})
useEffect(() => {
const fetchData = async() => {
}
fetchData()
return () => {
setData({})
}
}, [])
return <ApiContext.Provider value = {
{
repData: data,
loading,
repNumbers, //this is the state variable
setRepNumbers //this is the setState function
}
} > {
children
} <
/ApiContext.Provider>
}
In the consumming component
const { repData, repNumbers, setRepNumbers } = React.useContext(ApiContext)
const [pageLoading, setPageLoading] = React.useState(true)
useEffect(() => {
const fetchData = async () => {
setPageLoading(true)
await Axios({
})
.then((res) => {
setRepNumbers({...repNumbers, [id]: res.data })
setPageLoading(false)
return false
})
.catch((err) => {
return {}
})
return false
}
fetchData()
}, [])
If there are 2 consuming components, there should be 2 api calls and the repNumbers state should be mutated 2 times and add 2 id data but it only contains one id and if other call resolves, it replace the former id.
So how can I get both ids in repNumbers state?
This: ...but it only contains one id and if other call resolves, it replace the former id.
Assuming React 18 from your question. If so, React "Batches" updates. So although two updates were made, only the very last one was recorded.
This is dicussed in this blog post.
You can consider flushSync()
You might also consider refactoring your code to avoid the situation in the first place.
I am using usestate, but it not updating even if i am using useeffect. i wanted to show grid once call to backend is complete
const backendData: any = useSelector((state: RootState) => state.backendData);
const [isLoading, setIsLoading] = useState(false);
const [data, setBackendData] = useState(backendData);
//once call to backend is complete, backendData gets updated
useEffect(() => {
setBackendData(backendData);
setIsLoading(false);
}, [backendData]);
useEffect(() => {
setIsLoading(isLoading);
}, [isLoading]);
const onSearchButtonClick = () => {
setIsLoading(true); //when i set isloading to true it is not updating even if i have useeffect
//call to backend
getSearchDetails();
if (!isLoading) { //i wanted to show grid once call to backend is complete
//but this is always false and grid is displaying before call to backend is complete
setGridShow(true);
}
};
I have created a custom hook that fetches setting from an api that uses Async-Storage.
// Takes the key/name of the setting to retrieve
export const useSettings = (key) => {
// stores the json string result in the setting variable
const [setting, setSetting] = useState("");
const deviceStorage = useContext(DeviceStorageContext);
useEffect(() => {
getValue()
.then(value => setSetting(value));
}, []);
// gets value from my custom api that uses Async-Storage to handle r/w of the data.
const getValue = async () => await deviceStorage.getValueStored(key);
const setValue = async (value) => {
await deviceStorage.setValueStored(key, value);
getValue().then(value => setSetting(value));
};
const removeValue = async () => { }
return [setting, { setValue,removeValue }];
};
This works as expected in Main.jsx without any problem.
const Main = () => {
const [units, operations] = useSettings('units');
useEffect(() => {
const initSettings = async () => {
if (units) {
console.log(units)
return;
}
await operations.setValue({ pcs: 1, box: 20 });
};
initSettings();
}, []);
However, when I even just call the useSetting hook in Form.jsx and visit the page, it freezes my entire app to just that page.
const FormView = ({ handleReset, handleSubmit }) => {
const [setting,] = useSettings('units');
Removing the useState and useEffect fixes it and calling these methods directly works but I really don't want to call getValue() throughout my project and use async/await code to handle it.
Stuck on this for hours now. Any help will be appreciated.
Thank you.
It was a dropdown component library inside FormView that was messing it up. Removing that library fixed it.
In my React functional component, I have the following code;
const user = useFetch('api/userinfo', {});
Essentially, this is a custom hook call and internally it has a fetch call to the API and sets the data (below is relevant code inside usefetch);
const [data, setData] = useState(initialData);
//....fetch call
setData(json); // once data is fetched
In my main component, since my grid depends on this data, how do I make the code wait to proceed to the Grid jsx till data is fetched? I was planning to use async..await. But not sure if it is possible to do that here with custom hooks?
With below code, seems like the hooks is getting invoked multiple times for some reason;
export default function useFetch(initialUrl, initialData) {
const [url] = useState(initialUrl);
const [loadingData, setLoadingData] = useState(true);
const [data, setData] = useState(initialData);
useEffect(() => {
setLoadingData(true);
fetch(url)
.then(response => {
if (response.status === 200) {
response.json().then(json => {
setData(json);
setLoadingData(false);
});
})
}, [url]);
return [loadingData, data];
}
A couple options for you:
Use another state variable (ie some boolean) and use that to keep track of whether or not the data comes back from the API. Then conditionally render some 'loading' element
Check to see if the data exists and conditionally render based on its existence.
Here's how you can do it with your custom hook:
// defining useFetch hook
const useFetch = (url) => {
// state to keep track of loading
const [loadingData, setLoadingData] = useState(false);
// state for data itself
const [data, setData] = useState(null);
// effect to fetch data
useEffect(() => {
const fetchData = async () => {
try {
// set data to loading
setLoadingData(true);
// request to load data, you can use fetch API too
const { data } = await axios.get(url);
// set data in state and loading to false
setLoadingData(false);
setData(data);
} catch (error) {
console.log("error", error);
}
};
fetchData();
}, [url]);
// return the data and loading state from this hook
return [loadingData, data];
};
Now, you can use this hook in your component like:
const MyComponent = (props) => {
const [isDataLoading, data] = useFetch('/api/some-url');
// now check if data is loading, if loading then return a loader/spinner
if (isDataLoading || !data) return <p>Data is loading...</p>
// otherwise render your actual component
return (
<div>
<h1>This is my component with data</h1>
</div>
);
}
I am working on a small CRUD fullstack app with react and mongodb and I have this problem where I use useEffect to make an axios get request to the server to get all of my todos. The problem is that useEffect does it's job but it also rerenders to infinity. This is my component:
export default function () {
...
const [todos, setTodos] = useState([]);
const currentUser = JSON.parse(localStorage.getItem('user'))._id;
useEffect(() => {
async function populateTodos () {
try {
const res = await axios.get(`http://localhost:8000/api/all-todos/${currentUser}`);
setTodos(res.data);
} catch (err) {
if (err.response) {
console.log(err.response.data);
console.log(err.response.status);
console.log(err.response.headers);
} else if (err.request) {
console.log(err.request);
} else {
console.log('Error: ', err.message);
}
}
}
populateTodos();
}, [todos]);
console.log(todos);
return (
...
);
}
So what I was expecting to happen is that that console.log to get printed only when the todos changes, like when I add a new todo and so on, but instead it gets printed forever.
You said that you need to fetch todos at first, and whenever todos change. I can suggest you a different approach, using one more variable, something like this:
const TodosComponent = (props) => {
const [todos, setTodos] = useState([]);
const [updatedTodos, setUpdatesTodos] = useState(true);
const fetchFunction = () => {
// In here you implement your fetch, in which you call setTodos().
}
// Called on mount to fetch your todos.
useEffect(() => {
fetchFunction();
}, []);
// Used to updated todos when they have been updated.
useEffect(() => {
if (updatedTodos) {
fetchFunction();
setUpdatesTodos(false);
}
}, [updatedTodos]);
// Finally, wherever you update your todos, you also write `updateTodos(true)`.
}