I dispatch a custom action in react admin after a click on Upload button. POST request successfully works, but I don't know how to get a response. I log every reducer call, but no response data there. Example of my action:
export const UPLOAD_BY_BASE64 = 'UPLOAD_BY_BASE64';
export const uploadByBase64 = ({ file, path }) => ({
type: UPLOAD_BY_BASE64,
payload: { file, path },
meta: { fetch: CREATE, resource: 'images/upload/base64' }
});
According to the Handling Side Effects documentation, you can pass a callback to the meta attribute in order to retrieve the response payload:
export const uploadByBase64 = ({ file, path }) => ({
type: UPLOAD_BY_BASE64,
payload: { file, path },
meta: {
fetch: CREATE,
resource: 'images/upload/base64',
callback: ({ payload, requestPayload }) => {
// payload = response payload
},
},
});
Related
i've an app whcih sends email to users to reset their passwords
this is the link i send to the user email to be able to do password reset
http://localhost:8081/reset/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MmU2YWJmMmMzMzI0Mjk1NGQyNmVjZjIiLCJpYXQiOjE2NTk0ODkzODEsImV4cCI6MTY1OTQ5MDI4MX0.6omB-TkXXcwrjv0MaJQxltyERIoJZmkm8sY74AAqgxo
but each time i try to access the link i get
Cannot GET /reset/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
this is my route
{ name: "reset", path: "/reset/:token", component: Reset },
this is my script tag
<script>
import axios from "axios";
export default {
data: () => ({
valid: true,
password: "",
}),
mounted() {
console.log("the id is :" + this.$route.params.token);
},
methods: {
async handleSubmit() {
const response = await axios
.post("http://localhost:5000/api/auth/reset-password", {
newPassword: this.password,
token: this.$route.params.token
})
.then(res => {
console.log(res);
})
.catch(error => {
console.log(error);
});
},
}
};
</script>
please how can i go about these
$router.push("/") redirects to the Home Page correctly, but Home Page has to fetch new data, but it doesn't. It has the same data what it had previously.
It can possibly because of creating web history, but I want this functionality later on.
The home page fetches data from a JSON server.
export default {
name: "Home",
data() {
return {
todos: [],
};
},
components: {
SingleTodo,
},
mounted() {
fetch("http://localhost:3000/todos")
.then((res) => res.json())
.then((data) => (this.todos= data))
.catch((err) => console.log(err.message));
}
Which then returns a list to display.
I have added a new page to add data into that JSON server which works fine. After successfully pushing the data I want to redirects to the Home page. Which I am doing like the below snippet.
let todo = {
name: this.name,
details: this.details,
completed: false,
};
fetch("http://localhost:3000/todos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(todo),
})
.then(this.$router.push("/"))
.catch((err) => console.log(err.message));
This successfully stores the values into the JSON Server and $router.push("/") redirects it to the Home page but it doesn't refetch the data from the JSON server.
Router config:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import AddProject from '../views/AddProject.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/add',
name: 'AddProject',
component: AddProject
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
The problem is with JSON Server.
It helps to spin up a local server and watch a file for data. This is good for development purpose.
But what I noticed is
let todo = {
name: this.name,
details: this.details,
completed: false,
};
fetch("http://localhost:3000/todos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(todo),
})
.then(this.$router.push("/"))
.catch((err) => console.log(err.message));
in this that JSON Server successfully gets into .then() before completely updating the file. I then changed the code
.then(setTimeout(() => {
this.$router.push("/");
}, 1000))
and after redirecting I had all the values that were added.
I can also see it when I opened a browser and the data file side by side, the browser redirects first and then the file updates. (however, this is not the correct way to check, that's why I added setTimeout)
Thanks, Everyone for their contribution in the comments.
The problem was you used mounted() instead of created(). sometimes it won't work. Maybe other people will have better explanation to this. you can read more in official docs about vue life cycle
Another thing in your codes use methods instead of wring a function direct inmounted() or created() your codes should be like this.
methods:{
getTodo(){
fetch("http://localhost:3000/todos")
.then((res) => res.json())
.then((data) => (this.todos= data))
.catch((err) => console.log(err.message));
}
},
created(){
this.getTodo()
}
this make your code look cleaner and will not confuse you in the future
I have a user profile page with an image. I'm uploading the image to Firebase Storage, updating the user's record in Firestore to point at the image and displaying it. I have everything working in a single component.
Now I want to refactor to put the image uploading functionality into its own component with the idea that the new component will return the url of the uploaded file so that the parent component can then update Firestore. The benefit being that I can re-use the image upload component on other pages without the uploader needing to know why it's uploading images. The way I have architected this is as follows:
<template>
...
<v-btn
:disabled="!file"
class="primary"
small
#click.prevent="selected">Upload</v-btn>
...
</template>
export default {
name: "BaseImageUploader",
props: {
accept: {
type: String,
default: () => "image/png, image/jpeg, image/bmp"
},
placeholder: {
type: String,
default: () => "Click to select an image to upload"
},
label: {
type: String,
default: () => "Profile Picture"
}
},
data: () => ({
file: null
}),
methods: {
selected() {
const fileRef = storage.child("corporate-wellness-1/" + this.file.name);
fileRef
.put(this.file)
.then(uploadTaskSnapshot => uploadTaskSnapshot.ref.getDownloadURL())
.then(url => this.$root.$emit("base-image-uploader", url))
}
}
};
then the parent listens for the event and updates Firestore like so:
...
mounted() {
this.$root.$on(`base-image-uploader`, (event) => {
this.uploadImage(event)
});
},
...
uploadImage(url) {
this.$store
.dispatch("user/updateProfileImageUrl", url)
.then(() => console.log('image url updated in bio'))
}
The problem is I'm getting
ReferenceError: url is not defined
on the dispatch in the parent component.
The event only gets emitted after the url becomes available in the child component, so I don't understand why it's not available in the parent when the event handler is called.
So, I've two questions:
Why doesn't the code written work?
And more generally, is there a better way to architect it?
I call the react-navigation NavigationService within a redux action.
Testing the action I need to mock the navigate function.
/app/utils/NavigationService.js
import { NavigationActions } from 'react-navigation';
let navigator;
function setTopLevelNavigator(navigatorRef) {
navigator = navigatorRef;
}
function navigate(routeName, params) {
navigator.dispatch(NavigationActions.navigate({
type: NavigationActions.NAVIGATE,
routeName,
params,
}));
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
I created a __mock__ folder immediately adjacent to the NavigationService.js file.
app/utils/__mocks__/NavigationService.js UPDATED
const navigate = jest.fn();
const setTopLevelNavigator = jest.fn();
export default {
navigate,
setTopLevelNavigator,
};
Why doesn´t jest auto-mock the navigate function when the test is run?
https://jestjs.io/docs/en/manual-mocks
__tests__/actions/AuthActions.test.js UPDATED
jest.mock('../../app/utils/NavigationService'); //at the top directly behind other imports
it('should call firebase on signIn', () => {
const user = {
email: 'test#test.com',
password: 'sign',
};
const expected = [
{ type: types.LOGIN_USER },
{ payload: 1, type: types.DB_VERSION },
{ payload: 'prod', type: types.USER_TYPE },
{ payload: { name: 'data' }, type: types.WEEKPLAN_FETCH_SUCCESS },
{ payload: { name: 'data' }, type: types.RECIPELIBRARY_FETCH_SUCCESS },
{
payload: { user: { name: 'user' }, userVersionAndType: { dbVersion: 1, userType: 'prod' } },
type: types.LOGIN_USER_SUCCESS,
},
];
return store.dispatch(actions.loginUser(user)).then(() => {
expect(store.getActions()).toEqual(expected);
});
});
app/actions/AuthActions.js
export const loginUser = ({ email, password }) => (dispatch) => {
dispatch({ type: LOGIN_USER });
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.catch((signInError) => {
dispatch({ type: CREATE_USER, payload: signInError.message });
return firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(async (user) => {
const userVersionAndType = await dispatch(initUser());
await dispatch(initWeekplan(userVersionAndType));
await dispatch(initRecipeLibrary(userVersionAndType));
return user;
});
})
.then(async (user) => {
saveCredentials(email, password);
const userVersionAndType = await dispatch(getUserVersionAndType());
await dispatch(weekplanFetch(userVersionAndType));
await dispatch(recipeLibraryFetch(userVersionAndType));
dispatch(loginUserSuccess({ user, userVersionAndType }));
NavigationService.navigate('Home');
})
.catch(error => dispatch(loginUserFail(error.message)));
};
You've create a manual mock for a user module.
Activating a manual mock of a user module for a particular test file requires a call to jest.mock.
For this particular case add this line to the top of __tests__/actions/AuthActions.test.js and the mock will be used for all tests within that test file:
jest.mock('../../app/utils/NavigationService'); // use the manual mock in this test file
Note that manual mocks for user modules and Node core modules (like fs, path, util, etc.) both have to be activated for a particular test file by a call to jest.mock, and that this behavior is different than manual mocks for Node modules which are automatically applied to all tests.
Been trying to use storybook with my VueJS project and Im stuck with mocking api calls.
I tried using axios-mock-adapter without luck.
My storybook file code is:
import { storiesOf } from '#storybook/vue';
import { action } from '#storybook/addon-actions';
import { withKnobs, boolean } from '#storybook/addon-knobs';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import fileUpload from './fileUpload.vue';
const mock = new MockAdapter(axios);
mock
.onPost('https://s3.amazonaws.com')
.reply(200, []);
storiesOf('Common|File CSV Upload', module)
.addDecorator(withKnobs)
.add('Default', () => ({
components: { fileUpload },
data: () => ({
}),
template: `
<v-flex>
<file-upload></file-upload>
</v-flex>`,
methods: {
action: action('file upload'),
},
}));
Am I using it right?
My strong recommendation is to use storybook-addon-mock for mocking (axios) API calls in Storybook.
It is nicley integrated into Storybook, setup in the different stories is easy and the data can be alteresd in the corresponding panel.
These 4 steps are needed:
Add the additional dependency: yarn add storybook-addon-mock
adapt the config and add in .storybook/main.js:
module.exports = {
addons: [
...
'storybook-addon-mock',
],
configure the behaviour and add mock data for general/ repeating API calls in .storybook/preview.js. These are mocks that will be available in every story.
export const parameters: Parameters = {
mockAddonConfigs: {
globalMockData: [
{
url: 'api/token',
method: 'POST',
status: 200,
response: () => '1234567abcdefg',
},
],
refreshStoryOnUpdate: true, // This re-render the story if there's any data changes
// disable: true, // This disables the panel from all the stories
}
in your story file add:
export default {
title: 'components/myComponentName',
component: MyComponent,
parameters: {
mockData: [
{
url: '/api/myendpoint/',
method: 'GET',
status: 200,
delay: 500,
response: () => 'some content',
},
{
url: '/api/myendpoint/',
method: 'POST',
status: 200,
delay: 1000,
response: {
data: 'some response',
},
},
],
},
Hint: Have a look into the different responses - function, string, etc to match them with the real response. There can be a pitfall with the data entry, that can be avoided with response: () => 'some content'