I'm injecting the pixel script in the <Head /> and trying to use the fbq("track") function in a contact form's onSubmit handler. A dumbed down version of the handler:
const handleSubmit = async e => {
e.preventDefault();
try {
// send form data to server, if successful, proceed...
window.fbq("track", "Contact");
} catch (error) {
// handle error
}
};
In development, everything looks as expected: the Facebook Pixel Helper shows that the "Contact" event was successfully tracked.
However, in production, that event doesn't get tracked. The Pixel Helper registers a "Button Click Automatically Detected," but the explicit "Contact" event is not tracked. Edit: The title of my question may be a bit of a misnomer as the fbq("track", "PageView") call in the <Head /> is working normally.
No errors, it simply doesn't seem like it's firing.
Any ideas as to what I'm doing wrong would be much appreciated 🙏🏼
Solved.
Perhaps a bit more code context was needed for the issue. I still don't exactly know why this fix worked, but in case anybody stumbles across this in the future, here's what did it for me...
Additional context, the form data was posted to the server via fetch() and window.fbq("track") was chained into a .then() callback like so:
const handleSubmit = async e => {
e.preventDefault();
try {
await fetch("/contact", {options})
.then(res => res.json())
.then(json => {
if (json.success) {
// handle success
window.fbq("track", "Contact");
}
});
} catch (error) {
// handle error
}
};
The solution was simply to move the fbq() out of the callback:
const handleSubmit = async e => {
e.preventDefault();
try {
await fetch("/contact", {options})
.then(res => res.json())
.then(json => {
if (json.success) {
// handle success
}
});
} catch (error) {
// handle error
return;
}
window.fbq("track", "Contact");
};
Related
First, I POST the parent object and its ID comes back from the server. I need to then include that ID while POSTing the another post call .i am having difficulty in chaining the multiple api call.i having difficulty managing then and catch in react axios post?
code*
parent
var hold
axios.post(/,data)
.then((res) => {
hold=res.data.msg
.then(axios.post(/${hold},data1))
.catch((err) => {
toast.error("Error aayo add vayena");
});
})
.catch((err) => {
toast.error("Error cannot post");
});
You should consider using async/await syntax for handling chained promises, since it helps for handling them in a easier way, according to this article. However, you have to use this in an async function.
In your case, it could be something like this:
const fetchData = async () => {
try{
await axios.post(/,data).then(res => hold = res.data.msg)
}catch(err) {
console.log("Error cannot post");
}
try{
await axios.post(/${hold},data1)
}catch(err) {
console.log("Error aayo add vayena");
}
}
This way, the second post will be done once the first one is finished
I'm a completely new to the whole react world but I'm trying to develop a SPA with a integrated calendar. I'm using react-router for routing, react-big-calendar for the calendar, axios for my API calls and webpack.
Whenever I'm loading my Calender Component it gets mounted and unmounted several times and I think that causes my API call to never actually get any data. I just can't figure out what is causing this.
The Code:
useEffect(() => {
console.log("mounting Calendar")
let source = Axios.CancelToken.source()
if(!initialized) {
console.log("getting Data")
getCalendarEvents(source)
}
return () => {
console.log("unmounting Calendar")
source.cancel();
}
})
const getCalendarEvents = async source => {
setInitialized(true)
setLoading(true)
try {
const response = await getCalendar({cancelToken: source.token})
const evts = response.data.map(item => {
return {
...item,
}
})
calendarStore.setCalendarEvents(evts)
} catch (error) {
if(Axios.isCancel(error)){
console.log("caught cancel")
}else{
console.log(Object.keys(error), error.message)
}
}
setLoading(false)
}
This is the result when i render the component:
Console log
If you need any more code to assess the problem, I will post it.
I appreciate any kind of input to solve my problem.
Thank you
Its because of the useEffect. If you want it to run just once on mount you need to pass an empty array as a dependency like so :
useEffect(() => {
console.log("mounting Calendar")
let source = Axios.CancelToken.source()
if(!initialized) {
console.log("getting Data")
getCalendarEvents(source)
}
return () => {
console.log("unmounting Calendar")
source.cancel();
}
},[])
This means it will only run once. If there is some state or prop you would like to keep a watch on you could pass that in the array. What this means is that useEffect will watch for changes for whatever is passed in its dependency array and rerun if it detects a change. If its empty it will just run on mount.
Some data is fetched from an API if users click a button inside my React app. If another button is clicked before the API call has finished, the callback function should not be applied. Unfortunately, the state ("loading" in my code example) has not the correct value inside the callback. What am I doing wrong?
const [apartments, setApartments] = useState(emptyFeatureCollection);
const [loading, setLoading] = useState(true);
function getApartments() {
fetch(`https://any-api-endpoint.com`)
.then(response => response.json())
.then(data => {
if (loading) setApartments(data);
})
.catch(error => console.error(error));
}
}
useEffect(() => {
setLoading(false);
}, [apartments]);
function clickStartButton() {
setLoading(true);
getApartments();
}
function clickCancelButton() {
setLoading(false);
}
The issue here is that the callback code:
data => {
if (loading) setApartments(data);
}
is called in context of the original closure of getApartments() when loading was false.
That means that the callback only ever sees or "inherits" the prior loading state and, because setAppartments() relies on the updated loading state, the data from your network request is never applied.
A simple solution that requires minimal change to you code would be to pass a callback to setLoading(). Doing so would give you access to the current loading state (ie of the component rather than that of the closure in your your callback is executed). With that, you could then determine if appartment data should be updated:
function getApartments() {
/* Block fetch if currently loading */
if (loading) {
return;
}
/* Update loading state. Blocks future requests while this one
is in progress */
setLoading(true);
fetch(`https://any-api-endpoint.com`)
.then(response => response.json())
.then(data => {
/* Access the true current loading state via callback parameter */
setLoading(currLoading => {
/* currLoading is the true loading state of the component, ie
not that of the closure that getApartnment() was called */
if (currLoading) {
/* Set the apartments data seeing the component is in the
loading state */
setApartments(data);
}
/* Update the loading state to false */
return false;
});
})
.catch(error => {
console.error(error);
/* Reset loading state to false */
setLoading(false);
});
}
Here's a working example for you to see in action. Hope that helps!
I have developing mern stack web site. In that I have added below codes to handle logging.
onSubmit(e) {
e.preventDefault();
const obj = {
user_name: this.state.user_name,
password: this.state.password
};
axios.post('http://localhost:4000/login', obj)
.then(res=> localStorage.setItem('token',(res.data.token))
//localstorage.setItem('username','res.data.user.username)
)
}
When I click on login button this onSubmit() function called and will save token in local storage.
But, res.data have more details. (from backend it passes logged users information too)
So I want to add those to local storage. I tried that as commented in above function. It says error in res. Note : I user react for frontend.
Also I want to handle handle errors in any cases axios.post() didn't work as planned. In server side it send different messages for unmatched credentials and wrong passwords. How can I show those in my page. Thank you.
Since the only accepted data type in localStorage is string, you should stringify it first using JSON API.
const userDataStr = JSON.stringify(res.data);
localStorage.setItem('userData', userDataStr);
Now if you want to access the userData from localStorage you just need to convert it back to javascript object.
const userDataStr = localStorage.getItem('userData', userData);
const userData = JSON.parse(userDataStr);
You can have multiple catch in the returned promise of axios.post
axios.post()
.catch((error) => { })
.catch((error) => { })
But those catch will called with the same error so you need to handle it differently in each catch
Another suggestion:
If you want to easily handle the error, you can use higher order function like this
const handleError = (status, callback) => (error) => {
if (status === error) {
callback(error);
}
}
axios.post()
.catch(handleError(404, (error) => { /* only called when status === 404 */ }))
.catch(handleError(500, (error) => { /* only called when status === 500 */ }))
While working on a side project, I faced an issue with react-router-dom.
What I want to implement is: When I submit a Form, I need to save the data on my server. While the request is pending, I need to display a loading indicator. Once the server says everything is ok, I need to redirect the user on a new page
action.js
export const addNotification = value => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
} catch(e) {
dispatch(addNotificationFailure())
}
}
component.js
class CreateNotificationForm extends Component {
onSubmit = (values) => {
this.props.addNotification(parameters, history)
}
render() {
const { isCreating } = this.props
const submitBtnText = isCreating ? 'Creating...' : 'Submit'
return (
<Form>
// content omitted
<Submit value={submitBtnText} />
</Form>
)
}
}
const mapStateToProps = (state) => ({
isCreating: getIsFetching(state)
})
const mapDispatchToProps = (dispatch) => ({ // omitted })
connect(mapStateToProps, mapDispatchToProps)(CreateNotificationForm)
So far so good: When I submit my form, the form's submit button shows a Creating... text.
However, how do I tell react-router to load a new path once the request is successful?
Right now, I've done that by using withRouter and using this.props.history as a second argument for this.props.addNotification.
It works great, but it seems really wrong
I've seen solutions using react-router-redux, but I don't really want to add a new middleware to my store.
Should I make the API call inside my component and use a Promise?
Any help?
Update:
After working a little on my own React project, and thinking about similar situations where I handle route changes there, I decided I want to change my original answer. I think the callback solution is OK, but the solution that you already mentioned of making the API call inside your component and using a promise is better. I realized that I've actually been doing this in my own app for a while now.
I use redux-form in my app, and it provides onSubmitSuccess/onSubmitFail functions that you can use to handle the submit result, and each of those rely on you returning a promise (usually from your action creator).
I think the fact that one of the most popular packages for form submission in React/Redux supports this pattern is an indication that it's probably a good pattern to use. Also, since react-router passes history into your component, it seems logical that they expect most people to do a lot of their programmatic route changes inside the component.
Here's an example of what the promise solution would look like with your code:
action.js
export const addNotification = value => dispatch => {
return new Promise(async (resolve, reject) => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
resolve(response)
} catch(e) {
dispatch(addNotificationFailure())
reject(e)
}
})
}
component.js
onSubmit = async () => {
try {
await this.props.addNotification(parameters)
this.props.history.push('/new/route')
} catch(e) {
// could use a try/catch block here to display
// an error to the user here if addNotification fails,
// or go to a different route
}
}
Old Answer:
A simple solution would be to allow addNotification() to accept a callback function as an optional second argument.
export const addNotification = (value, callback=null) => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
(typeof callback === 'function') && callback()
} catch(e) {
dispatch(addNotificationFailure())
}
}
Then inside your component use the router to go to the new route.
onSubmit = (values) => {
this.props.addNotification(parameters, () => {
this.props.history.push('/new/route')
})
}
You should not write your asynchronous calls in reducers or actions as the documentation clearly suggests them to be pure functions. You will have to introduce a redux-middleware like redux-thunk or redux-saga (I personally prefer sagas)
All your async calls will happen inside the middleware, and when it succeeds, you can use react-routers history .replace() or .push() methods to update your route. Let me know if it makes sense
You can use one popular package axios
See Here https://www.npmjs.com/package/axios
and you can implement your login like
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
You can write your loader login while calling api
and then you can hide your loader in .then