How to change baseQuery redux when language changing with i18n? - react-native

`import {createApi,fetchBaseQuery} from '#reduxjs/toolkit/query/react'
import AsyncStorage from '#react-native-async-storage/async-storage';
import React, {useState,useEffect, useCallback} from 'react';
import useGetLanguage from '../Hooks/useGetLanguage'
import i18n from 'i18next';
import { fetchData } from './fetchData';
const customBaseQuery = async (url, options) => {
const language = await AsyncStorage.getItem('Language') == 1 ? 'en' : 'ru'
const baseUrl = http://oralbekov.dias19.fvds.ru/${language};
return fetchBaseQuery({ baseUrl })(url, options);
};
export const apiSlice = createApi({
baseQuery:customBaseQuery,
tagTypes: [
'PopularQuestions',
'CategoryQuestion',
'QuestionsCategory',
'CategoryTheory',
'CategoryTheoryArticle',
'Articles',
'ArticleById',
'HajjDays',
'HajjStartDate',
'UmrahDays',
'Translate',
'CategoryQuestions',
],
endpoints: (builder) => ({
getQuestionCategory: builder.query({
query: () => 'api/questions/categories/',
providesTags: ['QuestionsCategory']
}),
getPopularQuestions: builder.query({
query: () => 'api/questions/popular',
providesTags: ['PopularQuestions']
}),
getCategoryQuestions: builder.query({
query: (id) => `api/questions/category/${id}`,
providesTags: ['CategoryQuestions']
}),
getCategoryTheoryMain: builder.query({
query: () => `api/info/main`,
providesTags: ['CategoryTheory']
}),
getCategoryTheoryArticle: builder.query({
query: (id) => `api/info/${id}`,
providesTags: ['CategoryTheoryArticle']
}),
getArticles: builder.query({
query: () => `api/news/`,
providesTags: ['Articles']
}),
getArticleById: builder.query({
query: (id) => `api/news/${id}`,
providesTags: ['ArticleById']
}),
getHajjStartDate: builder.query({
query: () => `api/calendar/get_hajj_date`,
providesTags: ['HajjStartDate']
}),
getHajjDays: builder.query({
query: () => `api/calendar/hajj`,
providesTags: ['HajjDays']
}),
getUmrahDays: builder.query({
query: () => `api/calendar/umrah`,
providesTags: ['UmrahDays']
}),
getTranslate: builder.query({
query: () => `api/translate`,
providesTags: ['Translate']
}),
getTranslateById: builder.query({
query: (id) => `api/translate/categories/${id}`
}),
getPlaces: builder.query({
query: () => `api/maps/places`
}),
getRestaurants: builder.query({
query: () => `api/maps/restaurants`
}),
getRestaurantById: builder.query({
query: (id) => `api/maps/restaurants/${id}`
}),
getPlaceById: builder.query({
query: (id) => `api/maps/places/${id}`
}),
})
})`
export const {
useGetQuestionCategoryQuery,
useGetCategoryQuestionsQuery,
useGetPopularQuestionsQuery,
useGetCategoryTheoryMainQuery,
useGetArticlesQuery,
useGetArticleByIdQuery,
useGetCategoryTheoryArticleQuery,
useGetHajjStartDateQuery, useGetHajjDaysQuery, useGetUmrahDaysQuery,useGetTranslateQuery,useGetTranslateByIdQuery,
useGetPlacesQuery,useGetPlaceByIdQuery,useGetRestaurantsQuery,useGetRestaurantByIdQuery} = apiSlice
here is the code. I need to change baseQuery when AsyncSorage variable changing or i18n language changing (this the same thing).
This code works, but it doesnt changing dynamicly, i need to reload app and language are change
I tried apiSlice.updateBaseQuery but it doesnt help to me

Related

How can I add one loading using pending promises to handle all api loading in redux toolkit?

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js">
import { createSlice } from "#reduxjs/toolkit";
import { getUser, updateUser } from "./index";
import { getAllData, getData } from "../../logs/store/index";
const manageErrorAndLoading = (state, actionType, error) => {
state.loading[actionType] = true;
state.error[actionType] = error;
};
export const loadingSlice = createSlice({
name: "loading",
initialState: {
loading: false
},
reducer: {
toggleLoading: (state) => !state,
},
extraReducers: {
[getUser.pending]: () => true,
[getUser.fulfilled]: () => false,
[getUser.rejected]: () => false,
[updateUser.pending]: () => true,
[updateUser.fulfilled]: () => false,
[updateUser.rejected]: () => false,
[getData.pending]: () => true,
[getData.fulfilled]: () => false,
[getData.rejected]: () => false,
},
});
export const { toggleLoading } = loadingSlice.actions;
export default loadingSlice.reducer;
</script>
I have used this method to add loading but its not efficient. the loading is running parallel on all api, when one api is loading state gets true.
In the RTK matching utilities there is the addMatcher utility, which can catch all the pending, fulfilled and/or rejected promises. For example:
extraReducers(builder) {
builder.addMatcher(isPending, (state, action) => {
// do what you want when a promise is pending
})}
isPending can be replaced also with isRejected and isFulfilled.

mock store getters inside Global Guard does not work

I created a global guard that uses getters from store.
I am trying to mock some getters from store for testing purpose. The problem is that mocking
does not work.
// router/index.ts
export function beforeEach(to: any, from: any, next: any) {
const isLoggedIn = store.getters.isLoggedIn();
const isGuest = to.matched.some((record: any) => (record.meta.guest));
if (isLoggedIn) { // isLoggedIn is always false
if (isGuest) {
next('/');
}
}
}
}
//router/index.spec.ts
describe('shoud test routing functionality', () => {
it('should redirect to / when isLoggedIn is true and IsGuest is true', () => {
// given
jest.mock('#/store', () => ({
getters: {
isLoggedIn: jest.fn().mockImplementation(
() => true, // <----------- this value is always false
),
},
}));
// even this one does not work
// jest.spyOn(getters, 'isLoggedIn').mockImplementation(() =>
// ()=> true);
const to = {
matched: [{ meta: { guest: true } }],
};
const next = jest.fn();
// when
beforeEach(to, undefined, next);
// then
expect(next).toHaveBeenCalledWith('/');
});
})
I inspired from this example.
Thanks #EstusFlask comment, I solved the problem.
The keyword is that jest.mock inside a test can't affect top-level imports.
jest.mock('#/store', () => ({
getters: {
isLoggedIn: jest.fn(),
// other methods should be declared here, otherwise an exception is thrown
isSuperAdmin: jest.fn(),
isAdmin: jest.fn(),
isReadUser: jest.fn(),
},
}));
describe('should test routing functionality', () => {
it('should redirect to / when isLoggedIn is true and IsGuest is true', () => {
// given
store.getters.isLoggedIn.mockImplementation(() => () => false);
const to = {
matched: [{ meta: { guest: true } }],
};
const next = jest.fn();
// when
beforeEach(to, undefined, next);
// then
expect(next).toHaveBeenCalledWith('/');
});
})

jest config is throwing "type ErrorHandler = (error: mixed, isFatal: boolean) => void" after update to 26.x

i don't know why this is suddenly not working but this is my jest.config.js:
module.exports = {
preset: 'react-native',
verbose: true,
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ['./jestSetup.js'],
transformIgnorePatterns: [
'node_modules/(?!(jest-)?react-native|#react-native-community|#react-navigation)',
],
moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
},
};
and my jestSetup.js:
import 'react-native-gesture-handler/jestSetup';
import '#testing-library/jest-native/extend-expect';
import mockAsyncStorage from '#react-native-async-storage/async-storage/jest/async-storage-mock';
beforeAll(() => {
//#ts-ignore
global.__reanimatedWorkletInit = jest.fn();
});
jest.mock('react-native-share', () => ({
default: jest.fn(),
}));
jest.mock('#react-native-async-storage/async-storage', () => mockAsyncStorage);
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock');
Reanimated.default.call = () => {};
Reanimated.useSharedValue = jest.fn;
Reanimated.useAnimatedStyle = jest.fn;
return Reanimated;
});
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');
jest.mock('react-native-gesture-handler', () =>
jest.requireActual('../node_modules/react-native-gesture-handler/jestSetup')
);
jest.mock('react-native-intercom', () => {}, { virtual: true });
jest.mock('#react-native-async-storage/async-storage', () =>
require('#react-native-async-storage/async-storage/jest/async-storage-mock')
);
jest.mock('react-native-geolocation-service', () => ({
addListener: jest.fn(),
getCurrentPosition: jest.fn(),
removeListeners: jest.fn(),
requestAuthorization: jest.fn(),
setConfiguration: jest.fn(),
startObserving: jest.fn(),
stopObserving: jest.fn(),
}));
export const mockedNavigate = jest.fn();
jest.mock('#react-navigation/native', () => ({
...jest.requireActual('#react-navigation/native'),
useNavigation: () => ({
navigate: mockedNavigate,
goBack: jest.fn(),
}),
useRoute: () => ({
params: {
publicToken: 'testToken',
},
}),
}));
jest.mock('react-native-safe-area-context', () => {
const React = require('react');
class MockSafeAreaProvider extends React.Component {
render() {
const { children } = this.props;
return React.createElement('SafeAreaProvider', this.props, children);
}
}
return {
useSafeAreaInsets: () => ({ top: 1, right: 2, bottom: 3, left: 4 }),
SafeAreaProvider: MockSafeAreaProvider,
};
});
and my babel.config.js:
module.exports = {
presets: [
'module:metro-react-native-babel-preset',
['#babel/preset-env', { targets: { node: 'current' } }],
'#babel/preset-typescript',
],
plugins: [
'react-native-reanimated/plugin',
['relay', { artifactDirectory: './src/__generated__' }],
[
'transform-es2015-modules-commonjs',
{
allowTopLevelThis: true,
},
],
],
};
i have already looked at properly on the docs and can't figure out how this suddenly fails, below is the full error message:
node_modules/#react-native/polyfills/error-guard.js:14
type ErrorHandler = (error: mixed, isFatal: boolean) => void;
^^^^^^^^^^^^
SyntaxError: Unexpected identifier
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
at Object.<anonymous> (node_modules/react-native/jest/setup.js:453:6)
Solution found here. Add #react-native to your Jest configuration. Such as:
transformIgnorePatterns: [
'node_modules/(?!#react-native|react-native)'
],
I needed an extra ? after the # in the regex compared to the other solutions I found for this issue. Specifically, this is what I needed to add to my Jest configuration:
transformIgnorePatterns: [
'node_modules/(?!(jest-)?#?react-native|#react-native-community|#react-navigation)'
],
Inspired by: https://github.com/callstack/react-native-testing-library/issues/703
What worked for me was downgrading jest packages from 28.0.2 to 27.5.1
jest packages means:
jest
jest-cli
jest-circus
jest-environment-node

how is consept of vuex, if i manage many table is will be only one store.js

i really don't understand concept in vuex, example bellow i only handle one "todo table" there is many code there, i can not imagine if i handle other table like product,order,profile etc
is will write also in store.js
or there is other way if i want to handle many table
example code using vuex, only for one table
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
axios.defaults.baseURL = 'http://localhost:8000/api'
export const store = new Vuex.Store({
state:{
token: localStorage.getItem('access_token') || null,
filter: 'all',
todos: [
]
},
getters:{
loggedIn(state){
return state.token !== null
},
remaining(state) {
return state.todos.filter(todo => !todo.completed).length
},
anyRemaining(state,getters) {
return getters.remaining != 0
},
todosFiltered(state) {
if (state.filter == 'all') {
return state.todos
} else if (state.filter == 'active') {
return state.todos.filter(todo => !todo.completed)
} else if (state.filter == 'completed') {
return state.todos.filter(todo => todo.completed)
}
return state.todos
},
showClearCompletedButton(state) {
return state.todos.filter(todo => todo.completed).length > 0
}
},
mutations:{//harus dengan nama mutations
addTodo(state,todo){
state.todos.push({
id:todo.id,
title:todo.title,
completed:false,
editing:false
})
},
updateTodo(state, todo){
const index = state.todos.findIndex((item) => item.id == todo.id)
state.todos.splice(index, 1, {
'id': todo.id,
'title': todo.title,
'completed': todo.completed,
'editing': todo.editing,
})
},
deleteTodo(state,id){
const index = state.todos.findIndex((item) => item.id == id)
state.todos.splice(index, 1)
},
allChecked(state,checked){
state.todos.forEach(todo => (todo.completed = checked))
},
updateFilter(state, filter){
state.filter = filter
},
clearCompleted(state){
state.todos = state.todos.filter(todo => !todo.completed)
},
retreiveTodos(state,todos){
state.todos = todos
},
retrieveToken(state,token){
state.token = token
},
destroyToken(state){
state.token = null
},
clearTodos(state) {
state.todos = []
},
},
actions:{
clearTodos(context) {
context.commit('clearTodos')
},
register(context, data) {
return new Promise((resolve, reject) => {
axios.post('/register', {
name: data.name,
email: data.email,
password: data.password,
})
.then(response => {
resolve(response)
})
.catch(error => {
reject(error)
})
})
},
destroyToken(context){
if(context.getters.loggedIn){
localStorage.removeItem('access_token')
context.commit('destroyToken')
}
},
retrieveToken(context,credentials){
return new Promise((resolve,reject) => {
axios.post('/login',{
email: credentials.email,
password: credentials.password,
})
.then(response => {
const token = response.data.data[0].token;
localStorage.setItem('access_token',token)// hasil token di simpan di local storage
context.commit('retrieveToken',token)
resolve(response)
})
.catch(error => {console.log(error)
reject(error)
})
})
},
retreiveTodos(context){
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
axios.get('/todos')
.then(response => {
context.commit('retreiveTodos',response.data)
})
.catch(error => {console.log(error)})
},
addTodo(context,todo){
axios.post('/todos',{
title: todo.title,
completed: false
})
.then(response => {
context.commit('addTodo',response.data)
})
.catch(error => {console.log(error)})
},
updateTodo(context, todo){
axios.patch('/todos/' + todo.id,{
title: todo.title,
completed: todo.completed
})
.then(response => {
context.commit('updateTodo',response.data)
})
.catch(error => {console.log(error)})
},
deleteTodo(context,id){
axios.delete('/todos/' + id)
.then(response => {
context.commit('deleteTodo',id)
})
.catch(error => {console.log(error)})
},
allChecked(context,checked){
axios.patch('/todosCheckAll',{
completed: checked
})
.then(response => {
context.commit('allChecked',checked)
})
.catch(error => {console.log(error)})
},
updateFilter(context, filter){
context.commit('updateFilter',filter)
},
clearCompleted(context){
const completed = context.state.todos
.filter(todo => todo.completed)
.map(todo => todo.id)
axios.delete('/todosDeleteCompleted',{
data:{
todos:completed
}
})
.then(response => {
context.commit('clearCompleted')
})
.catch(error => {console.log(error)})
}
}
})

jest tests for Axios.all and Axios.spread

I am struggling to write JEST test cases for below method
getStudentList (studentList:}[]) {
if (studentList.length < 1) {
Promise.resolve()
}
let promises = []
for (const student of StudentList) {
if (!student.name) {
Promise.resolve()
}
var url = `${API_URL}/${student.name}/`
promises.push(Axios.get(url}))
}
return Axios.all(promises)
.then(Axios.spread((...args) => {
// customise the response here
return args
.map(response => response.data)
.map(data => {
//do something with data
return data
})
}))
It uses axios.all and axios.spread to get the data back..i have written simple test cases for Axios.get..but how to write test case for this? This method is in a vue project in a service class
This is a short example of how you can write your expectations (with 100% coverage) for the code above:
import myService from './myService';
import Axios from 'axios';
jest.mock('axios');
global.API_URL = 'http://example.com/mock_api';
describe('myService', () => {
describe('getStudentList', () => {
describe('without students in the list', () => {
it('should result undefined', () => {
const result = myService.getStudentList();
expect(result).resolves.toEqual( undefined );
});
});
describe('with students in the list', () => {
const mockStudentList = [{
name: 'student1',
}, {
someProp: 'some value',
}, {
name: 'student3',
}];
const results = [];
const mockAxiosSpreadResult = jest.fn();
beforeAll(() => {
Axios.get.mockClear();
Axios.all.mockResolvedValue(results);
Axios.spread.mockReturnValue(mockAxiosSpreadResult);
myService.getStudentList( mockStudentList );
});
it('should call Axios.get once for each student with name', () => {
expect(Axios.get).toHaveBeenCalledWith(`${API_URL}/student1/`);
expect(Axios.get).toHaveBeenCalledWith(`${API_URL}/student3/`);
});
it('should call Axios.spread with a callback', () => {
expect(Axios.spread).toHaveBeenCalledWith(expect.any(Function));
});
it('should call the result of Axios.spread with the resolved value of Axios.all', () => {
expect(mockAxiosSpreadResult).toHaveBeenCalledWith(results);
});
describe('Axios.spread callback', () => {
let callback;
beforeAll(() => {
callback = Axios.spread.mock.calls[0][0];
});
describe('called with parameters', () => {
let result;
beforeAll(() => {
result = callback({
data: 1
},{
data: 2
},{
data: 3
},{
data: 4
});
});
it('should do something with the data', () => {
expect(result).toEqual([1,2,3,4]);
});
});
});
});
});
});
working example