Plotting markers on a map using react-native-maps - javascript

In short, I'm trying to plot markers on a map using react-native-maps.
I've gone as far as creating an action to fetch the coordinates and respective ID from the server (see code below).
export const getPlacesOnMap = () => {
return dispatch => {
dispatch(authGetToken())
.then(token => {
return fetch("myApp?auth=" + token);
})
.catch(() => {
alert("No valid token found!");
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw(new Error());
}
})
.then(parsedRes => {
const places = [];
for (let key in parsedRes) {
places.push({
// ...parsedRes[key], // this fetches all the data
latitude: parsedRes[key].location.latitude,
longitude: parsedRes[key].location.longitude,
id: key
});
} console.log(places)
dispatch(mapPlaces(places));
})
.catch(err => {
alert("Oops! Something went wrong, sorry! :/");
console.log(err);
});
};
};
export const mapPlaces = places => {
return {
type: MAP_PLACES,
places: places
};
};
I don't know if I'm using the right words, but I've essentially tested the code (above) using componentWillMount(), and it successfully returned multiple coordinates as an array of objects.
Now, the problem is I don't know what to do next. As much as I understand, I know the end goal is to create a setState(). But I don't know how to get there.
Would be a great help if someone can point me in the right direction.

You need to create an async action. You can dispatch different actions inside an async action based on whether the async function inside it is resolved or rejected.
export function getPlacesOnMap(token) {
return async function(dispatch) {
dispatch({
type: "FETCHING_PLACES_PENDING"
});
fetch("myApp?auth=" + token)
.then(res => {
dispatch({
type: "FETCHING_PLACES_FULFILLED",
payload: res.json()
});
})
.catch(error => {
dispatch({
type: "FETCHING_PLACES_REJECTED",
payload: error
});
});
};
}
If your authGetToken() function is also a promise, you need to dispatch this action after authGetToken() was resolved.
You can use the action.payload in your "FETCHING_PLACES_FULFILLED" case of your reducer(s) to be able to use the retrieved data.
UPDATE
Your reducer should be like this:
export default function reducer(
state = {
loadingMarkers : false,
markers : [],
error : null,
},
action
) {
switch (action.type) {
case "FETCHING_PLACES_PENDING":
return { ...state, loadingMarkers: true };
case "FETCHING_PLACES_FULFILLED":
return { ...state, loadingMarkers: false, markers: action.payload};
case "FETCHING_PLACES_REJECTED":
return { ...state, loadingMarkers: false, error: action.payload };
default:
return state;
}
}
Now you can connect your component to redux and use your markers when they are fetched.
have a look at this example and connect docs

Related

useSWRInfinite with pagination and mutate features

I'm using useSWR to fetch data from client side in nextjs.
What I am doing and trying to achieve
I am using useSWRInfinite for the pagination feature and trying to update comments like state with bound mutate function with optimisticData option since I wanted to refresh the data immediately.(client-side perspective)
-> https://swr.vercel.app/docs/mutation#optimistic-updates and then get a new updated comment from axios and replace it with a previous comment that should be updated.
Expected
The data from useSWRInfinite should be updated right away since I am using optimisticData option until the API call is done and I could've set revalidate option to true but an async function in the mutate returns updated data with the response from axios. I didn't need it.
Actual behaviour
Even though I am passing optimisticData to the mutate, It doesn't update the data immediately. It keeps waiting until The API call is done and then gets updated.
What I've tried
I have tried using just normal useSWR function without the pagination feature and it worked well as I expected.
const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite<CommentType[]>(
(index) => `/api/comment?postId=${postId}&currentPage=${index + 1}`,
fetcher,
{ revalidateFirstPage: false }
);
const likeCommentHandler = async (commentId: string, dislike: boolean) => {
const optimisticData = data?.map((comments) => {
return comments.map((comment) => {
if (comment.id === commentId) {
if (dislike) {
--comment._count.likedBy;
comment.likedByIds = comment.likedByIds.filter(
(likeById) => likeById !== session!.user.id
);
} else {
comment.likedByIds.push(session!.user.id);
++comment._count.likedBy;
}
return { ...comment };
} else {
return { ...comment };
}
});
});
mutate(
async (data) => {
const { data: result } = await axios.post("/api/likeComment", {
commentId: commentId,
userId: session?.user.id,
dislike,
});
const newData = data?.map((comments) => {
return comments.map((comment) => {
if (comment.id === result.comment.id) {
return result.comment;
} else {
return comment;
}
});
});
return newData;
},
{ optimisticData, revalidate: false, populateCache: true }
);
};

Cleaning up a useEffect warning with useReducer,

I keep getting these warnings:
Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup
For some of my useEffects that pull data from an API with the help of my useReducer:
export default function HomeBucketsExample(props) {
const {mobileView} = props
const [allDemoBuckets, dispatchAllBuckets] = useReducer(reducer, initialStateAllBuckets)
const ListLoading = LoadingComponent(HomeBucketLists);
useEffect(() =>
{
getAllDemoBuckets(dispatchAllBuckets);
}, [])
return (
<ListLoading mobileView={ mobileView} isLoading={allDemoBuckets.loading} buckets={allDemoBuckets.data} />
);
}
However, Im not sure how to clean up this effect above, I've tried mounting it using True and False, however the error still showed up. How can I fix my function above so the useEffect doesnt throw any warnings
EDIT:
code for my reduer:
export const getAllDemoBuckets = (dispatch) => axiosInstance
.get('demo/all/')
.then(response => {
dispatch({ type: 'FETCH_SUCCESS', payload: response.data })
console.log('fired bucket-data')
})
.catch(error => {
dispatch({ type: 'FETCH_ERROR' })
})
const initialStateAllBuckets = {
loading: true,
error: '',
data: []
}
const reducer = (state, action) =>
{
switch (action.type)
{
case 'FETCH_SUCCESS':
return {
loading: false,
data: action.payload,
error: ''
}
case 'FETCH_ERROR':
return {
loading: false,
data: {},
error: "Something went wrong!"
}
default:
return state
}
}
const [allDemoBuckets, dispatchAllBuckets] = useReducer(reducer, initialStateAllBuckets)
The goal of the warning is to tell you that some action is taking place after the component is unmounted and that the result of that work is going to be thrown away.
The solution isn't to try and work around it with a reducer; the solution is to cancel whatever is happening by returning a callback from useEffect. For example:
useEffect(() => {
const ctrl = new AbortController();
fetchExternalResource(ctrl.signal);
return () => {
ctrl.abort();
}
}, []);
Using flags to determine if a component is mounted (ie using a reducer) to determine whether or not to update state is missing the point of the warning.
It's also okay to leave the warning up if this isn't actually an issue. It's just there to nit pick and tell you that, hey, you may want to clean this up. But it's not an error.
In your case, if you are using fetch, I would modify your code such that the function that dispatches actions can take an AbortSignal to cancel its operations. If you're not using fetch, there's not much you can do, and you should just ignore this warning. It's not a big deal.
It looks like you're using Axios for your requests. Axios supports a mechanism similar to abort signals - This should do the trick.
import { CancelToken } from 'axios';
const getAllDemoBuckets = async (dispatch, cancelToken) => {
try {
const response = await axiosInstance.get('/demo/all', { cancelToken });
dispatch({ type: 'FETCH_SUCCESS', payload: response.data });
} catch (err) {
if ('isCancel' in err && err.isCancel()) {
return;
}
dispatch({ type: 'FETCH_ERROR' });
}
}
const MyComponent = () => {
useEffect(() => {
const source = CancelToken.source();
getAllDemoBuckets(dispatch, source.token);
return () => {
source.cancel();
};
}, []);
}

Vue store dispatch error response not being passed to UI

I'm trying to get the error response from my Vue store dispatch method, into my component, so I can tell the user if the save failed or not.
store/userDetails.js
const state = {
loading: {
user_details: false,
}
}
const getters = {
// Getters
}
const actions = {
save({commit, dispatch, rootState}, payload) {
commit('setLoading', {name: 'users', value: true});
axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
).then((response) => {
if (response.data) {
commit('setState', {name: 'user_details', value: response.data.units});
commit('setLoading', {name: 'user_details', value: false});
dispatch(
'CommonSettings/setSavingStatus',
{components: {userDetails: "done"}},
{root:true}
);
}
}).catch((error)=> {
console.log(error)
return error
}
)
}
My component method
views/Users.vue
send() {
this.$store.dispatch({
type: 'Users/save',
userDetails: this.current
}).then(response => {
console.log(response)
});
},
Above, I'm logging out the response in two places.
The response in my store/userDetails.js file is logged out fine, but it's not being passed to my send() function in my component - it comes up as undefined. Any reason why it wouldn't be passed through? Is this the correct way to do this?
This works for me. Try this solution.
store.js
actions: {
save(context, payload) {
console.log(payload);
return new Promise((resolve, reject) => {
axios(url)
.then((response) => {
resolve(response);
})
.catch((error) => {
reject(error);
});
});
},
},
My Component method
App.vue
save(){
this.$store.dispatch("save", dataSendToApi).then((response)=>{
console.log(response)
})
}
Try returning axios call in the Store Action:
// add return
return axios(
_prepareRequest('post', api_endpoints.user.details, rootState.token, payload)
)
.then() // your stuff here
.catch() // your stuff here
If that won't work, use Promise in the Store Action. Like this:
return new Promise((resolve, reject) => {
return axios() // simplify for readibility reason, do your stuff here
.then((response) => {
//... your stuff here
resolve(response) // add this line
})
.catch((error) => {
// ... your stuff here
reject(error) // add this line
})
})
you should return a promise, reference link:vue doc

Using a callback while dispatching actions

I am trying to make a GET request for some data.
Here is my action call.
componentDidMount() {
this.props.fetchData(() => {
this.setState({ isLoading: false });
});
}
Prior to completion I'd like to display "Loading..." momentarily as the fetch request is making it's trip. I'm using a callback for this and setting my local state.
Here is my action creator with a 'callback'.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then(() => callback())
.catch((err) => {
console.log(err.message);
});
}
}
And here is that same function above but dispatching the action so that I can receive as props and render to my ui.
export function fetchData(callback) {
return (dispatch) => {
axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => {
console.log(err.message);
});
}
}
My question is how do you make the callback and dispatch the action in the same action creator function? Is that even good practice?
You could do something like this
componentDidMount() {
this.setState({ isLoading: true }, () => {
// ensuring that you make the API request only
// after the local state `isLoading` is set to `true`
this.props.fetchData().then(() => this.setState({ isLoading: false });
});
}
and, fetchData would be defined as follows
export function fetchData(callback) {
return (dispatch) => {
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: FETCH_DATA, payload: response }))
.catch((err) => console.log(err.message));
}
}
If you're using the redux-thunk middleware to use asynchronous actions, then these actions will return Promises; so you can set your component's local state after that Promise resolves.
In the component:
.....
componentDidMount() {
this.props.fetchData();
}
....
export default connect((state) => ({loading: state.loading, data: state.data}))(Component);
In the actions, you should do :
....
export function fetchData() {
return (dispatch) => {
dispatch({ type: FETCHING_DATA}); //dispatch an action for loading state to set it to true
return axios.get(`/api/fetchsomething`)
.then((response) => dispatch({ type: DATA_FETCHED, payload: response }))
.catch((err) => console.log(err.message));
}
}
....
In the reducer, you should do :
....
case 'FETCHING_DATA':
return {
...state,
loading: true,
}
case 'DATA_FETCHED':
return {
...state,
data: action.payload,
loading: false,
}
....
I personally feel that you shouldn't put any business logic in your component because it can cause some problems later when you want to refactor your app. This means that there shouldn't be any .then in your component and everything should be guided through redux (if there is some side effects in your app). So, you should control your loading state from redux itself and not inside the component.

Parse XML in react-redux-promise app

The data source for my app only provides data in XML format.
I use axios to get the XML data. It ends up as a string in the data section of the result.
I have tried to use xml2js to convert it, but it just fires off a async job and returns, so I dont get the redux-promise middelware to work. The payload is nothing when the reducers sends the data to the component that should render it.
Not sure if this makes sense, but can I make the reducer wait for the new function call to return before sending the data on the the component?
action index.js
export function fetchData(jobid, dest) {
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
const request = axios.get(url);
console.log(request);
return {
type: FETCH_DATA,
payload: request
}
}
my reducer
export default function (state = [], action) {
console.log(action);
switch (action.type) {
case FETCH_DATA:
console.log("pre");
parseString(action.payload.data, function (err, result) {
// Do I need some magic here??? or somewhere else?
console.dir(result);
});
return [action.payload.data, ...state];
}
return state;
}
you should change your action creator code, because axios is async. And dispatch action after receive data.
You don't need this logic in reducer.
For async actions you may use redux-thunk
export const fetchData = (jobid, dest)=>dispatch =>{
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
const request = axios.get(url).then(res=>{
parseString(res, function (err, result) {
if(result){
dispatch({
type: FETCH_DATA,
data:result
})
}
if(err) throw err
});
}).catch(err=>console.error(error))
};
///clean reducer
export default function (state = [], action) {
switch (action.type) {
case FETCH_DATA:
return [...state, action.data ];
}
return state;
}
Also you may need to know about fetching process: loading, success , failure.Then action creator may looks like:
export const fetchData = (jobid, dest)=>dispatch =>{
const url = `${DATA_URL}jobid=${jobid}&refdist=${dest}`;
dispatch({
type: FETCH_DATA_REQUEST,
data:result,
isFetching:true
})
const request = axios.get(url).then(res=>{
parseString(res, function (err, result) {
if(result){
dispatch({
type: FETCH_DATA_SUCCESS,
data:result,
isFetching:false
})
}
if(err) throw err
});
}).catch(err=>{
dispatch({
type: FETCH_DATA_FAILURE,
err:err,
isFetching:false
})
console.error(error)
})
};

Categories