I'm able to send get and put methods fine, but am surprisingly not able to send a delete fetch request from my Redux action to my Rails backend. This is even more perplexing because in Postman I'm able to hit the Destroy route fine. I've searched all over for a fix, but haven't found anything that works. I have an onClick function that triggers the Redux action that sends this request:
export const deleteQuestion = (questionId, routerHistory) => {
return dispatch => {
return fetch(`${API_URL}/questions/${questionId}`, {
method: 'DELETE',
}).then(response => {
dispatch(removeQuestion(questionId));
routerHistory.replace(`/`);
})
.catch(error => console.log(error));
};
};
I've checked numerous times to make sure the syntax and route is fine. questionId is also the correct question ID. However, no matter what I do, the Destroy method in the Questions controller won't recognize the request. I've checked the route in Rails and it exists. I don't get any errors, no request is ever sent to the server, and nothing is returned, not in terminal, the console, or anything.
This is the Github account: https://github.com/jwolfe890/react_project1
I'd really appreciate any insight anyone has. Thank you!
Your deleteQuestion method returns an anonymous function with a dispatch parameter which never seems to be called (Calling code). Only deleteQuestion is called but not the function returned by it.
Because it is called by a click handler I'd say you actually want something like this:
export const deleteQuestion = (questionId, routerHistory) => {
fetch(`${API_URL}/questions/${questionId}`, {
method: 'DELETE',
}).then(response => {
dispatch(removeQuestion(questionId));
routerHistory.replace(`/`);
})
.catch(error => console.log(error));
};
Or if you want to return the promise, you could of course change it to:
export const deleteQuestion = (questionId, routerHistory) => {
return fetch(`${API_URL}/questions/${questionId}`, {
method: 'DELETE',
}).then(response => {
dispatch(removeQuestion(questionId));
routerHistory.replace(`/`);
})
.catch(error => console.log(error));
};
If you want to dynamically inject the dispatch function, you could leave your original code, but would have to call the method like this:
deleteQuestion(this.state.question.id, history)(myDispatchMethod);
Related
I am trying to send data from the main process to the renderer and the ipcRenderer.on function is being executed but the data is undefined...
This is the code to send the data:
mainWindow.webContents.on("did-finish-load", () => {
let name = "test";
mainWindow.webContents.send("updateSave", name);
})
And this to receive it:
ipcRenderer.on("updateSave", (e, data) => {
console.log("data: " + data);
})
The console.log is beeing fired but it just says: data: undefined
And this is the preload file:
contextBridge.exposeInMainWorld("ipcRenderer", {
send: (channel, ...args) => ipcRenderer.send(channel, ...args),
on: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args))
});
Does anybody know what I did wrong? Thanks
In your preload.js, you're defining ipcRenderer.on() as follows:
on: (channel, func) => {
ipcRenderer.on(
channel,
(event, ...args) => {
func(...args)
}
)
}
Thus, event will never get passed to func() and is thus swallowed. Now, there are two possible solutions (personally, I'd go with (1) because it's easiest to implement and will make for cleaner code on both parts. However, please see the remark below):
(1) You want to keep the signature in your renderer
I.e., you want to still write ipcRenderer.on("channel", (event, data) => {});. Then you will have to change your preload code. I suggest to simply pass func to ipcRenderer like so:
on: (channel, func) => ipcRenderer.on (channel, func)
Then, any func parameter must have the signature (event, ...data) => {}.
(2) You want to keep the signature in your preload
I.e., you want to keep the code of your preload contextBridge definition. Then, any instance of ipcRenderer.on() in your renderer code will need to change to fit the signature (...data) => {} since there's no event parameter passed to the function.
As a side note, it is considered a best practice if you don't expose Electron API function (such as ipcRenderer.on(), ipcRenderer.send() etc) to the renderer when proxying using a preload script, but instead provide discrete functions for your renderer code to choose from, as shown in Electron's official IPC tutorial.
I'm using Formik to create a form for a React web app. The submission is as the following code.
const submitForm = (values) => {
console.log(JSON.stringify(values, null, 2));
setFormStatus(status.loading);
// handle request.
axios
.put("#", values)
.then(() => {
console.log("Submission Success");
setFormStatus(status.success);
})
.catch(() => {
console.log(`Submission Failure`);
setFormStatus(status.failure);
})
.then(() => {
console.log("Submission CleanUp");
setTimeout(() => {
console.log("Neutralizing Form");
setFormStatus(status.neutral);
// tell to animate.
setSwitchSubmitBtn(!switchSubmitBtn);
}, 2000);
});
};
After the axis request, there will be a 2s delay before I set the status state back to neutral.
However, I'm testing using Jest, waitFor doesn't work as expected. The timeout in submission seems blocked, as no matter how long I wait for the submission, it just won't occur. I found a solution to this by using jest.advanceTimer, but adding the same amount of delay in waitFor doesn't work.
it("INPUT_FORM_TC_008", async () => {
jest.useFakeTimers();
render(<InputForm />);
axios.put.mockImplementation(async () => {
console.log("MOCKED PUT");
return Promise.reject();
});
// click submit Button
user.click(screen.getByTestId("submitBtn"));
await waitFor(() => {
expect(screen.queryByTestId("crossIcon")).not.toBeNull();
});
act(() => {
jest.advanceTimersByTime(2000);
});
await waitFor(() => {
// crossIcon will be unmounted once `status` changes to neutral.
expect(screen.queryByTestId("crossIcon")).toBeNull();
});
});
The following code won't work if I don't use jest.advanceTimer, even I set timeout much longer than the one in submission.
await waitFor(() => {
expect(screen.queryByTestId("crossIcon")).toBeNull();
}, 5000);
I suspect that this is related to the event loop stuff, but I tried to set the timeout to be 20ms in the submission and it works. So I looking for a reason why this happens.
I tried to test a state change using setTimeOut using Jest. I expect after the amount of time I specified in setTimeOut the state should be changed. But using waitFor won't work even set a longer timeout.
The issue is that you are setting a setTimeout in a scope that is immediately exited. This code should still work when it's actually run due to a closure on your function, but in Jest the test will say, "welp, looks like there's no more synchronous code" and finish the test before waiting for the execution.
If you want more information on why setTimeout behaves this way you can check the MDN article here: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
But putting it very simply, it uses something called the "event loop" which is how JS handles asynchronous code while still being a synchronous language. When you use setTimeout, you're simply adding the function execution to the event loop and saying, "Don't execute this code now, run other code until the 2000 ms have gone by, then run this code." Which works fine in your implementation because you're allowing 2000ms to go by. But Jest doesn't wait, it just says "There's no more code to execute and nothing is ready in the event loop, the test must be complete" and cleaning everything up immediately.
You could resolve this by wrapping your setTimeout in a promise, like:
//........
.then(() => {
console.log("Submission CleanUp");
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Neutralizing Form");
resolve(status.neutral);
}, 2000);
})
});
Then have your actual implementation code wait for the promise to resolve and set your formStatus and submissionButton states after the Promise is resolved. This would be a simpler API, would allow for greater abstraction, and would make testing easier since you'd now be able to wait for this execution to finish with a .then() block in your tests as well.
I am using axios on a React w/ Hooks front end to make a get request to populate my react-google-maps/api GoogleMaps Marker components using seed data in my rails backend. When I let the rails server run, the server repeatedly makes this call.
The following line causes the axios.get to be called on a loop:
React.useEffect(() => {
// Get Coordinates from api
// Update Coordinates in state
axios.get('/api/v1/coordinates.json')
.then(response => response.data.data.map(coord =>
setCoordinateFromApi(coord.attributes)))
.catch(error => console.log(error))
}, [coordinates.length])
This successfully populates the map but means I can't use onClick's functionality (because I presume the stack is being topped with this request?)
My index method on my CoordinatesController in Rails:
def index
coordinates = Coordinate.all
render json: CoordinateSerializer.new(coordinates).serialized_json
end
NB: this is my first project linking React to Rails as well as using Hooks
I would assume that you have this useState defined above:
const [coordinated, setCoordinatesFromApi] = useState([])
If it is, then this is the root cause:
React.useEffect(() => {
axios.get(...).then(coordinates => setCoordinateFromApi(coord.attributes))
}, [coordinates.length])
By doing so, you ask React.useEffect to always call axios.get whenever coordinates.length change. Which will make this useEffect an infinite loop (because you always change the coordinates value whenever the axios request finish).
If you only want to execute it once, you should just pass an empty array on the useEffect, like this
React.useEffect(() => {
axios.get(...).then(coordinates => setCoordinateFromApi(coord.attributes))
}, [])
That way, your axios.get will only be called once and you will no longer have infinite loop
I'm integrating a CRM with Facebook lead Ads using Zapier, and I can create a lead in Facebook and create it in the CRM without any issues.
After a successful post (i.e. successfully creating the lead), I'm curious what I should be returning, I would have thought
return Ok();
would have been enough. Instead, I get an error message saying:
- Got a non-object result, expected an object from create ()
What happened (You are seeing this because you are an admin):
Executing creates.ZapLead.operation.perform with bundle
Invalid API Response:
- Got a non-object result, expected an object from create ()
What should I be returning?
Code which makes the post is:
perform: (z, bundle) => {
const promise = z.request({
url: 'https://{{bundle.authData.subdomain}}.ngrok.io/api/zapier/create/lead/' + bundle.inputData.lead_type + '/' + bundle.inputData.core_customerTypeId,
method: 'POST',
body: JSON.stringify({
fields: bundle.inputData
}),
headers: {
'content-type': 'application/json'
}
});
// return promise.then((response) => JSON.parse(response.content));
return promise.then((response) => {
if (response.status != 200) {
throw new Error(`Unexpected status code ${response.status}`);
}
});
Any ideas?
Thanks,
David here, from the Zapier Platform team.
While your answer is technically correct, I wanted to add some context about the error and why your solution works.
Zapier expects a javascript object (basically anything valid and wrapped in {}) to come out of a create function. That's why JSON.parse(response.content) works, it's returning whatever the server said. Your code throws an error if it's not a 200, but doesn't return anything if it is a 200. Since undefined is not of type Object, the error you're seeing is thrown.
While {"Success":"Success","Attempt":null,"Id":null,"RequestId":null} is totally a valid response (it's an object!), it's more useful for the end-user to return data about the new lead that was created. That way, it can be used downstream for other actions.
Let me know if you've got any other questions!
As a side note, we're very open to how to make that error message more clear; it's one devs struggle with a lot.
I think I found it. Looks like I need to return this if successful:
{"Success":"Success","Attempt":null,"Id":null,"RequestId":null}
I'm trying to use the github-oauth2 provider in Torii, but I'm stumped on how I'm supposed to se tup some of the callbacks. I'll trace the code I'm using, as well as my understanding of it, and hopefully that can help pinpoint where I'm going wrong.
First, in my action, I'm calling torii's open method as it says to do in the docs:
this.get('torii').open('github-oauth2').then((data) => {
this.transitionTo('dashboard')
})
And, of course, I have the following setup in my config/environment.js:
var ENV = {
torii: {
// a 'session' property will be injected on routes and controllers
sessionServiceName: 'session',
providers: {
'github-oauth2': {
apiKey: 'my key',
redirectUri: 'http://127.0.0.1:3000/github_auth'
}
}
},
}
The redirectUri is for my Rails server. I have the same redirectUri setup on my github app, so they match.
Here's what I have on my server. It's likely this is where the problem is. I'll get to the symptoms at the end.
def github
client_id = 'my id'
client_secret = 'my secret'
code = params[:code]
#result = HTTParty.post("https://github.com/login/oauth/access_token?client_id=#{client_id}&client_secret=#{client_secret}&code=#{code}")
#access_token = #result.parsed_response.split('&')[0].split('=')[1]
render json: {access_token: #access_token}
end
So I post to github's access_token endpoint, as I'm supposed to, and I get back a result with an access token. Then I package up that access token as json.
The result of this is that the torii popup goes to the rails page:
Unfortunately, what I was hoping for was for the torii popup to disappear, give my app the access_token, and for the code to move on and execute the code in my then block.
Where am I going wrong?
Many thanks to Kevin Pfefferle, who helped me solve this and shared the code to his app (gitzoom) where he had implemented a solution.
So the first fix is to clear my redirectUri, and to set it on github to localhost:4200. This made the app redirect so that it's an Ember app that it's redirected to.
The second fix was to create a custom torii provider
//app/torii-providers/github.js
import Ember from 'ember';
import GitHubOauth2Provider from 'torii/providers/github-oauth2';
export default GitHubOauth2Provider.extend({
ajax: Ember.inject.service(),
fetch(data) {
return data;
},
open() {
return this._super().then((toriiData) => {
const authCode = toriiData.authorizationCode;
const serverUrl = `/github_auth?code=${authCode}`;
return this.get('ajax').request(serverUrl)
.then((data) => {
toriiData.accessToken = data.token;
return toriiData;
});
});
}
});
Not sure why this then triggers but the then I was using before didn't. Anyways, it grabs the data and returns it, and then the promise I was using before gets the data correctly.
this.get('torii').open('github-oauth2').then((data) => {
//do signon stuff with the data here
this.transitionTo('dashboard')
})
So there we go! Hopefully this helps other folks who are stuck in the future.