React testing use mockstore with custom store to test it - reactjs

I would like to test components that are using redux store in my project. Here is the function that I use fo getting some store elements:
const variable = store.getState().serverConfiguration.testValue;
When I am testing this component I use mockStore to do that so my store is empty right? That is why I get error saying that TypeError: Cannot read property 'testValue' of undefined
Is there a way to pass my custom store to mockStore so that it will be possible to test it correctly?
I tried to do that like this but it doesn't work:
const store = configureMockStore()({
serverConfiguration: {
testValue: "Admin"
}
});

Try this:
import configureStore from "redux-mock-store"
const mockStore = configureStore([]);
const store = mockStore({
serverConfiguration: {
testValue: "Admin"
}
})

Related

Try to dispatch a Thunk from a Redux store dispatch. Error Argument of type 'MyThunk' is not assignable to parameter of type 'AnyAction'

I'm using Next.js and I've got a complex admin form page that I need to use a 2nd Redux store. I know this is almost never needed, but I really like the way Redux handles the state updates. In my form, I'll have many actions and some of them are async, because some of the fields are image uploads. I don't want to create a slice for that page in my global store (which handles stuff like auth, user and other global data).
So I'm creating the new store it by using the following code:
import { configureStore, EnhancedStore } from "#reduxjs/toolkit";
import { useMemo } from "react";
import adminBlogPostSlice from "./adminBlogPostSlice";
export const useAdminBlogPostStore = () : EnhancedStore => {
const store = useMemo(() => {
return configureStore({
reducer: adminBlogPostSlice,
devTools: false
});
},[]);
return store;
};
So, in my page container I'm doing:
const AdminBlogPostContainer: React.FC<AdminBlogPostContainer> = (props) => {
const { mode } = props;
const { slug } = useRouter().query;
const store = useAdminBlogPostStore();
useEffect(() => {
const { dispatch } = store;
dispatch(THUNK.LOAD({mode,slug}));
},[mode,store,slug]);
return(
<Provider store={store}> // PROVIDING THE STORE TO THE REST OF THE PAGE
<AdminBlogPostPage/>
</Provider>
);
};
But I'm getting error on the Thunk dispatch call.
This is my Thunk type. I mean, THUNK.LOAD (the one that is being dispatched) is of type AdminBlogPostThunk
type AdminBlogPostThunk = ThunkAction<
Promise<void> | void,
AdminBlogPostState,
unknown,
AnyAction
>
It seems to work fine one level below by using the useDispatch hook. I'm not able to use this hook on AdminBlogPostContainer because that's where the Provider is being added.
I saw this page in the official docs: https://redux.js.org/recipes/usage-with-typescript#define-root-state-and-dispatch-types
What am I doing wrong? Is it possible to use the store the way I'm trying to? Any other ideas?
Don't declare the hook as returning EnhancedStore. Let TS infer the return type. That way TS should figure out that "it's a Redux store, but with the thunk middleware actually included".

How to access the Redux store outside of a component in React

I am beginning with Redux and I always used it in components with connect() and mapStateToProps(), but now I want to call my API with setInterval() every x time to check if the server has new data not stored in the Redux store, and substitute it.
My approach was to create a function that reads the store and update it like that:
import { store } from './dir/dir/store.js'
const refresher = async () => {
const state = store.getState();
// Call API, compare 'state.calendar' with calendar from server
// Call store.dispatch() if they are different, then update Redux store
}
export default refresher
My questions are:
Is this a good practise to use Redux?
Is there a better approach to this problem?
Thanks
It's perfectly fine to export the store and use within a vanilla js/ts file.
Example Redux Store
Make sure to export the store that you create
import { configureStore } from "#reduxjs/toolkit";
import { slice } from "../features/counterSlice";
export const store = configureStore({
reducer: {
counter: slice.reducer
}
});
Usage in Non-Component Code:
You can then import the created store in any other code
import { store } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function getCount(): number {
const state = store.getState();
return state.counter.value;
}
export function incrementCount() {
store.dispatch(counterSlice.actions.increment());
}
Traditional Usage in Functional Component
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../App/store";
import { slice as counterSlice } from "../features/counterSlice";
export function Clicker() {
const dispatch = useDispatch();
const count = useSelector((state: RootState) => state.counter.value);
const dispatchIncrement = () => dispatch(counterSlice.actions.increment())
// ...
Example Slice
import { createSlice } from "#reduxjs/toolkit";
export const slice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
}
}
});
Demo in Codesandbox
Note: You cannot use this option with Server Side Rendering. If you need to support SSR, you can use middleware to listen to dispatched actions and handle elsewhere.
Further Reading
What is the best way to access redux store outside a react component? | Stack Overflow
Access the Redux Store Outside a React Component | Blog
How can I access the store in non react components? | Github Issues
Here you can access the store and action out side any component like index.js file in react-native.
import {updateLocationAlertItem} from './src/store/actions/locationAlertAction';
import {store} from './src/store/index';
store.subscribe(listener);
function listener() {
state = store.getState().locationAlertReducer;
}
store.dispatch(
updateLocationAlertItem({
index: index,
is_in_radius: true,
is_notification: true,
arrival_time: moment().format('DD/MM/YYYY hh:mm'),
exit_time: item.exit_time,
}),
);

How do I access the redux store from a helper class or function

I have the access token stored on the Redux store and I want to send it with each API request where authentication is needed. I have a class for API functions. Which is kind of a library. I am not sure how I can access the token from the API class.
Can anyone help regarding this?
You have to import store in the file you need to access the store from your respective index file, i have done it like this
import { store } from '../index';
store.subscribe(() => {
store.getState();
token = store.getState().Login.token; //access token here
})
In the index.android.js/index.ios.js i have exported store
export const store = createStore(
RootReducer,
undefined,
// the ordering of middleware is significant.
applyMiddleware(thunk),
);
If you need have access from your component and considering you have a reducer called user and in your initial state you have something like this:
const INITIAL_STATE = {
logged: false
}
1. import { connect } from 'react-redux'
Create your mapStateToProps like:
const mapStateToProps = (state) => {
return {
logged: state.user.logged
}
}
As a good practice create a prop in your component:
MyComponent.propTypes = {
logged: PropTypes.bool
}

Redux state value globally

I am having a helper class which provies info about the logged in user. I would like to have it as a static method and use it in several places ( which are not react components)
How to get access the redux store value in a class model ?
If you can manage this in your application it would be cleaner (and more testable) to pass it around with dependency injection (DI) as opposed to using a global variable, static or singleton. In a simple form you could just pass it in the constructor like this:
var store = createStore(...);
var app = new AppClass(store);
If you're not using a bundler like Webpack, Lukas Katayama's answer should work window.store = createStore(...) but if you are using one, you can also expose your redux store by exporting it and importing where you need it.
//store.js
export const store = createStore(...);
//other file
import { store } from './store.js';
const someFuncOrClass = (...) => {
const user = store.getState().userInfo;
}
One way could be set a global variable
window.store = createStore(...)
and use it, accessing window.store
Looks like a straight forward implementation. You can create a getter for store in the same file where you creatStore(...).
store.js
import { createStore } from 'redux';
let store = {};
export default function store(...) {
/*
* things to do before creating store
*/
store = createStore(...);
return store;
}
export function getStore() {
return store;
}
helper.js
import { getStore } from './store';
export function getUserInfo() {
const store = getStore();
const globalState = store.getState();
return globalState.userInfo;
}

Accessing redux store inside functions

I would prefer to have a function exposed from a .js file , within that function I would prefer to have access to the variables in the store.
Snippet of the code : -
import { connect } from 'react-redux';
function log(logMessage) {
const {environment} = this.props;
console.debug('environment' + environment + logMessage );
....
}
function mapStateToProps(state) {
return {
environment : state.authReducer.environment
};
}
export default function connect(mapStateToProps)(log);
I have many components, which attach the class through connect, can I attach functions through connect()?
Edit 1
Some_File.js
import store from './redux/store.js';
function aFunction(){
var newState =store.getState();
console.log('state changed');
}
store.subscribe(aFunction)
I am assuming you have created store and reducers as redux expects.
~~~~~~~~~~~~~~~
Original Answer Starts
This is a sort of hack, I don't know what you are doing so I can't say you should or you should not do it, but you can do it this way. I have copy-pasted some of your code with some modifications.
Class XYZ extends React.Component{
componentWillReceiveProps(props){
//in your case this.props.storeCopy is redux state.
//this function will be called every time state changes
}
render(){
return null;
}
}
function mapStateToProps(state) {
return {
storeCopy : state
};
}
export default function connect(mapStateToProps)(XYZ);
Put this component somewhere at top, may just inside provider, whenever state changes this componentWillReceiveProps of this component will be invoked.
If you have a pure functional component then you can access the redux state directly like this:
import store from './redux/store';
function getStoreDetails() {
console.log(store.getState());
}
The proper place to access the store is through a container, connect is used to connect a container to a component, you cannot connect a random function to it.
There is a logger middleware for redux that you might wan't to take a look at, it does what you're trying to achieve.
To use it, just pass it as a middleware to your store:
import createLogger from 'redux-logger';
const store = createStore(
reducer,
applyMiddleware(logger)
);
A more proper way to debug a redux app is to use React Dev Tools, if you use Chrome, I recommend you to use the React Dev Tools Extension. Just install it and use it as a middleware
let store = createStore(reducer, window.devToolsExtension && window.devToolsExtension());
With it, at any given moment you can see the whole state of your store, see the actions being fired and how they affect the store, and even rewind your application by un-doing actions.
Yes. You can attach functions via connect as below;
const mapDispatchToProps = (dispatch) => {
return {
testFunction: (param1) => dispatch(testFunction(param1)),
testFunction1: () => dispatch(testFunction1())
};
};
export default function connect(mapStateToProps, mapDispatchToProps)(log);
redux state can be accessed as prop in a function by using below format.
1:
import { connect } from 'react-redux';
// log accepts logMessage as a prop
function log(props) {
const { environment, logMessage } = props;
console.debug('environment' + environment + logMessage );
....
}
function mapStateToProps(state) {
return {
environment : state.authReducer.environment
};
}
export default connect(mapStateToProps)(log);
How to use log function?
log({ logMessage: "Error message" });
2:
// import redux store
import { store } from './Store';
function log(logMessage) {
const currentState = store.getState();
const environment = currentState.authReducer.environment;
console.debug('environment' + environment + logMessage);
....
}

Resources