I don't understand a bit how to make such a video correctly.
The cookie stores data, and when entering the page, it should be checked for access
/page/post.ts/
import CheckToken from '../../../utils/CheckToken';
export async function getServerSideProps(context) {
CheckToken(context.req, context.res);
return {
props: {
tabledb,setting
}, // will be passed to the page component as props
}
}
export default function post() {
return ()
}
..../../../utils/CheckToken
export default async function handler(req,res) {
try {
...//Data
} catch(err) {
res.redirect(401,"/admin/login" )
}
}
If it fails or if there is an error, I need to redirect to the authorization page
But res.redirect doesn't work outside of the api. Tell me then how to make a check then?
Your CheckToken function should return some kind of result to indicate if the call was successful or not, then based on that you can do a redirect.
export async function getServerSideProps(context) {
const result = CheckToken(context.req, context.res);
// if "falsy" do the redirect
if (!result) {
return {
redirect: {
destination: '/signin',
permanent: false,
},
}
}
return {
props: {
tabledb,setting
}, // will be passed to the page component as props
}
}
Related
I'm using redux-saga in my React application. However, I'm a bit confused of how call and put effects should be implemented in the right way.
I want to add new data to my redux state, but whenever I use the set up listed bellow, new transactions object appears in my UI, but not in the JSON Server. If I refresh the page, the newly added item disappears.
One of the reasons my set up looks exactly like this is that I read that actions should be implemented with put effect whenever dispatching an action and that it's good for testing your Redux Saga.
What is the right way to use put effect in my situation?
I have the following set up:
Redux Saga
import { put, call, take, takeLatest} from "redux-saga/effects";
import {
ADD_TRANSACTION,
addTransaction
} from "state/transactions";
// POST
async function addTransactionData(payload) {
try {
const response = await fetch(`http://localhost:3010/transactions`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const result = await response.json(payload);
return result;
} catch (error) {
throw error;
}
}
function* onAddTransactionData({ payload }) {
try {
const data = yield call(addTransactionData, payload);
yield put(addTransaction(data));
} catch (error) {
console.log(error);
}
}
export function* addTransactionWatcherSaga() {
yield take(ADD_TRANSACTION, onAddTransactionData);
}
Root Saga
import { all, call } from "redux-saga/effects";
import {
transactionsWatcherSaga,
removeTransactionWatcherSaga,
addTransactionWatcherSaga
} from "state/transactions";
export function* rootSaga() {
yield all([
call(transactionsWatcherSaga),
call(removeTransactionWatcherSaga),
call(addTransactionWatcherSaga)
]);
}
Action creators
// ....
export const addTransaction = ({
name,
amount,
description,
isExpense
} = {}) => {
return {
type: ADD_TRANSACTION,
payload: {
id: uuidv4(),
name,
amount,
description,
isExpense
}
};
};
//...
Reducer
/* ==============================================
============== TRANSACTIONS REDUCER ============
=============================================== */
import {
ADD_TRANSACTION,
REMOVE_TRANSACTION,
EDIT_TRANSACTION,
GET_TRANSACTIONS,
SET_TRANSACTIONS
} from "state/transactions";
const initialState = {
transactions: [],
transactionsLoader: false,
error: {
message: ""
}
};
export const transactionsReducer = (state = initialState, action) => {
switch (action.type) {
// ...
case ADD_TRANSACTION:
return {
...state,
transactions: [...state.transactions, action.payload]
};
// ....
default:
return state;
}
};
The only way I can succesfully dispatch a POST request is this, but that does not include put effect.
// POST
async function addTransactionData(payload) {
try {
const response = await fetch(`http://localhost:3010/transactions`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
const result = await response.json(payload);
console.log(result);
return result;
} catch (error) {
throw error;
}
}
function* onAddTransactionData({ payload }) {
console.log(payload);
try {
yield call(addTransactionData, payload);
} catch (error) {
console.log(error);
}
}
export function* addTransactionWatcherSaga() {
yield takeLatest(ADD_TRANSACTION, onAddTransactionData);
}
Your saga is not configured correctly. Every time a ADD_TRANSACTION action happens you are starting a saga for onAddTransactionData but onAddTransactionData ultimately puts the ADD_TRANSACTION action, so you will loop (unless you get an error from the API).
Typically you'd have an action like ADD_TRANSACTION which would start a saga onAddTransactionData. That saga would make the API call and get back the result. Then the saga would put an action like LOAD_TRANSACTION_RESULTS that would have the result of the API call in the payload, and your reducer would update your state with that result.
I have created a redux that is going to request an API and if the result is 200, I want to redirect the user to another page using history.
The problem is: I don't know how to trigger this change if the action is a success.
I could redirect the user in my useCase function but I can't use history.push pathName/state argument because it only works in a React component.
So this is what I have done in my React component:
const acceptProposalHandler = () => {
store.dispatch(acceptProposal(id)).then(() => {
setTimeout(() => {
if (isAccepted) { //isAccepted is false by default but is changed to true if the
//request is 200
history.push({
pathname: urls.proposal,
state: {
starterTab: formatMessage({id: 'proposalList.tabs.negotiation'}),
},
});
}
}, 3000);
});
};
Sometimes it works but other times it wont. For some reason, .then is called even if the request fails.
I'm using setTimeOut because if I don't, it will just skip the if statement because the redux hasn't updated the state with isAccepted yet.
This is my useCase function from redux:
export const acceptProposal = (id: string) => async (
dispatch: Dispatch<any>,
getState: () => RootState,
) => {
const {auth} = getState();
const data = {
proposalId: id,
};
dispatch(actions.acceptProposal());
try {
await API.put(`/propostas/change-proposal-status/`, data, {
headers: {
version: 'v1',
'Content-Type': 'application/json',
},
});
dispatch(actions.acceptProposalSuccess());
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
}
};
What I'm doing wrong? I'm using Redux with thunk but I'm not familiar with it.
".then is called even if the request fails." <- this is because acceptProposal is catching the API error and not re-throwing it. If an async function does not throw an error, it will resolve (i.e. call the .then). It can re-throw the error so callers will see an error:
export const acceptProposal = (id: string) => async (
// ... other code hidden
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
// ADD: re-throw the error so the caller can use `.catch` or `try/catch`
throw error;
}
};
I have got a component which might not request an ajax call if some data has been passed into it. However if the data hasn't been passed in I need to fetch it, so I want to import Axios then, save importing it for no reason.
How can I wait for the script to be imported before attempting to use it, as the below doesn't work:
export default {
props: {
vehicleId: {
type: Number|String,
required: true,
default: () => null
},
settings: {
type: Object,
default: () => null
}
},
beforeCreate() {
if (!this.settings) {
const Axios = () => import('../../../axiosConfig');
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
}
},
Dynamic import return a Promise, so you must to use then function.
Try something like that:
<script>
export default {
beforeCreate() {
if (!this.settings) {
import('../../../axiosConfig').then(axios => {
axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
});
}
},
};
</script>
Avoid the approach with async/await because the lifecycle functions don't support asynchronous in Vue.js.
You're almost there, import() is async, so just do:
// or use .then if you're not in an async function
const Axios = (await import('../../../axiosConfig')).default
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
and notice that import() returns the module, so you need to get the .default property if you need the default export (like in your case) or just call .someExportedName for importing a named export (i.e. non-default export from the module)
I have separated my api call into three layers. The component, the repository, and the apihelper. I want the logic for refresh_tokens to be in apihelper.js. When I do this it seems like the apihelper runs again after getting the 401 response status but it never passes the data back up to the component. I know I could put the logic to rerun it in the component but that seems like it will end up being a lot of duplicate code as I add more calls. I feel like it's probably caused by my shallow understanding of javascript promises but I'm a javascript beginner.
Component
<script>
import breweryrepository from '#/repository/breweryrepository.js'
export default {
mounted() {
this._getTotalBreweries();
},
methods: {
_getTotalBreweries() {
breweryrepository.getTotalBreweries()
.then((response) => {
if(response.data)
{
this.totalNumberOfBreweries = response.data.totalBreweries;
}
})
}
},
data () {
return {
totalNumberOfBreweries: ''
}
}
}
</script>
Repository
import apihelper from '#/helpers/ApiHelper.js';
export default {
getTotalBreweries() {
return new Promise(function(resolve, reject) {
resolve(apihelper.apiCall('/brewery/totalnumber'));
});
}
}
Apihelper
import axios from 'axios';
var querystring = require('querystring');
import { store } from '../store/store.js';
import auth from '#/auth/auth.js'
export default {
apiCall(url) {
return axios.get(store.state.baseUrl + url, { 'headers': auth.getAuthHeader() })
.catch((error) => {
if(error.response.status == 401)
{
console.log("401 error, running refresh and apicall again");
auth.refreshToken();
this.apiCall(url);
}
})
}
}
Aaaaand I wasn't returning the call.
return this.apiCall(url);
Works now
I am trying to make my Vue app have server-side rendering. I am using vue-server-renderer (https://www.npmjs.com/package/vue-server-renderer). Client-side rendering is working fine.
My app use vue-router and axios
Here is my server.js:
server.get('*', (request, response) => {
bundleRenderer.renderToString({ url: request.url }, (error, htmlPromise) => {
if (error) {
// Log the error in the console
console.error(error)
// Tell the client something went wrong
return response
.status(500)
.send(error)
}
response.send(layout.replace('<div id=app></div>', htmlPromise))
})
})
getInfo() is the method to fetch server data.
Here is getInfo():
export default {
methods: {
getInfo(api) {
return axios
.get(api || this.$route.params.path)
.then((data) => {
this.data = data
this.$set(this, 'isLoading', false)
})
},
},
}
My server entry is:
import { app, router, store } from './index'
export default context => {
let componentPromises = router.getMatchedComponents().filter((component) => {
return component.methods && component.methods.getInfo
}).map((component) => {
return component.methods.getInfo()
})
return Promise.all(componentPromises).then(() => {
return app
})
}
However, I soon realize that all the components from router.getMatchedComponents() does not have $route or $set. Therefore, the method getInfo() stops working.
The document from https://router.vuejs.org/en/api/router-instance.html is very short and does not provide much information:
router.getMatchedComponents()
Returns an Array of the components (definition/constructor, not
instances) matched by the current route. This is mostly used during
server-side rendering to perform data prefetching.
How can I fix the problem?
I have previously incurred into a similar problem and managed to successfully prefetch data by doing the following:
app.$router.onReady(() => {
const matchedComponents = app.$router.getMatchedComponents()
if (!matchedComponents.length) { /* ... */}
Promise.all(matchedComponents.map((Component: any) => {
if (Component.options.methods.asyncData) {
return Component.options.methods.asyncData({
store: app.$store,
route: app.$router.currentRoute
});
}
})).then(() => { /* your callback here ... */ });
}
According to vue ssr documentation (https://ssr.vuejs.org/en/data.html) the suggested way is to use a custom asyncData method in your component to perform data fetching rather than calling component methods directly:
export default {
asyncData ({ store, route }) {
// return the Promise from the action
return store.dispatch('fetchItem', route.params.id)
}
},