I am having problems while testing, and am not quite sure how to fix it:
Actions must be plain objects. Use custom middleware for async
actions.
13 |
14 | useEffect( () => {
> 15 | dispatch(getPostsAction())
| ^
16 | }, [])
I am using jest + enzyme, this is my test : Post.test.js
import React from 'react';
import { mount } from 'enzyme';
import Post from './Post';
import { storeFactory } from 'testUtils';
import { Provider } from 'react-redux';
jest.mock("store/actions/PostsAction")
import {getPostsAction as mockGetPostsAction} from 'store/actions/PostsAction'
const setup = (initialState = {})=>{
const store = storeFactory(initialState);
const wrapper = mount(<Provider store={store}><Post /></Provider>)
return wrapper;
}
test('getPostsAction on Post mount', ()=>{
const wrapper = setup()
expect(mockGetPostsAction).toHaveBeenCalledTimes(1)
})
This is my Post component:
import React,{useEffect} from "react"
import {useSelector, useDispatch} from 'react-redux'
import {getPostsAction} from 'store/actions/PostsAction'
function Post() {
useEffect( () => {
dispatch(getPostsAction())
}, [])
const posts= useSelector(state => state.posts.posts)
}
.....
export default Post;
This is my action: PostsAction.js
export const getPostsAction = ()=>{
return async (dispatch) =>{
try{
const {data} = await $http.get('/api/posts')
dispatch(setPosts(data.data))
}catch(e){
}
}
}
const setPosts = posts => ({
type: SET_POSTS,
payload: posts
})
EDITED:
testUtils.js
import checkPropTypes from 'check-prop-types';
import { middlewares } from 'store/store';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from 'store/reducers';
.....
export const storeFactory = (initialState) => {
return createStore(rootReducer, initialState, applyMiddleware(...middlewares));
}
store/store.js
import {createStore, applyMiddleware, compose} from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducers'
export const middlewares = [thunk];
const store = createStore(
reducer,
{},
applyMiddleware(...middlewares)
);
export default store
How could I fix the error? thank you.
Related
I am new to using react and also new to redux, I am working on a project which uses Django for Back-end and React for Front-end. I want to use redux for data state management but i can not seem to be doing it right. The problem is that the request is reaching the back end and also i have been having trouble handling the axios returned promise.
Here is my action transaction.js function:
import axios from "axios";
export const CREATE_TRANSACTION = "CREATE_TRANSACTION";
export const VIEW_TRANSACTION = "VIEW_TRANSACTION";
export const UPDATE_TRANSACTION = "UPDATE_TRANSACTION";
export const LIST_TRANSACTIONS = "LIST_TRANSACTIONS";
export const DELETE_TRANSACTION = "DELETE+TRANSACTION";
export const GET_TRANSACTIONLIST_DATA = "GET_TRANSACTIONLIST_DATA";
const ROOT_URL = "http://127.0.0.1:8000/api/";
export const getTransactionList = () => (dispatch) => {
axios
.get(`${ROOT_URL}transactions/`, {
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
dispatch({ type: LIST_TRANSACTIONS, payload: response.data });
console.log(response.data);
});
};
Here is my reducer transactions.js function:
import { LIST_TRANSACTIONS } from "../actions/transaction";
export function TransactionReducer(state = {}, action) {
switch (action.type) {
case LIST_TRANSACTIONS:
return action.payload;
default:
return state;
}
}
My store.js:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ latency: 0 })
: compose;
const store = createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(thunk))
);
export default store;
Trying to display data using useSelector()
import React, { useEffect } from "react";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
import { useSelector, useDispatch } from "react-redux";
import { getTransactionList } from "../../actions/transaction";
export default function TransactionList() {
const dispatch = useDispatch();
const data = useSelector((state) => state.transactions);
console.log(data);
useEffect(() => {
dispatch(getTransactionList());
}, [dispatch]);
return (
<TableContainer component={Paper} sx={{ boxShadow: "none" }}>
Lastly my index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
{" "}
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
reportWebVitals();
Please any help would be greatly appreciated, REMINDER I AM NEW TO USING THESE TECHNOLOGIES. Any further information required can be provided.
I see a more or less correct setup. The only part I think is missing is when you do this:
const store = createStore(
rootReducer,
initialState,
composeEnhancers(applyMiddleware(thunk))
);
Where is your rootReducer? I mean, I'm missing your root reducer code with something like that:
import { combineReducers } from 'redux';
import { TransactionReducer } from 'your/path/TransactionReducer';
import { fooReducer } from 'your/path/fooReducer';
import { barReducer } from 'your/path/barReducer';
export const rootReducer = combineReducers({
transactions: TransactionReducer,
foo: fooReducer,
bar: barReducer,
});
It isn't it?
Good morning everyone,
I just set up redux on my react project. What i am willing to do is to get data when the component Dashboard is rendered. Data are expenses and incomes variables, which are the sum of all the expenses and incomes.
The problem i get is that my reducer is never triggered. I get no error in my console.
As you can see in actions/dashboard.js, i consoled log when the call to fetchdata is done, and is working correctly. but the call to setBalance does not.
In the reducers/dashboard.js, in the switch case, the default case is always triggered, it's like it does not find the action type.
My Component Dashboard.js
import React, {useEffect} from 'react'
import classes from './Dashboad.module.css'
import { Doughnut } from 'react-chartjs-2'
import {connect} from 'react-redux'
import * as actions from '../../store/actions/index'
const DashBoard = (props) => {
useEffect(() => {
props.onFetchData()
}, [])
// ----------------------------------- Hidden code to not overload the topic -------------------
const mapStateToProps = state => {
return {
incomes: state.incomes,
expenses: state.expenses
}
}
const mapDispatchToProps = dispatch => {
return {
onFetchData: () => dispatch(actions.fetchData),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DashBoard);
actions/index.js :
export {
fetchData
} from './dashboard'
actions/actionsTypes.js :
export const SET_BALANCE = 'SET_BALANCE'
actions/Dashboard.js
import * as actionsTypes from './actionsTypes'
import axios from '../../axios'
export const setBalance = (donnees) => {
console.log('setbalance')
return {
type: actionsTypes.SET_BALANCE,
donnees: donnees
}
}
export const fetchData = () => {
console.log('call fetchdata')
return dispatch => {
axios.get('/movements.json')
.then(response => {
dispatch(setBalance(response.data))
console.log(response.data)
})
.catch(error => console.log(error))
}
}
Reducers/Dashboard.js :
import * as actionsTypes from '../actions/actionsTypes'
const initialState = {
incomes: 0,
expenses: 0
}
const setBalance = (state, action) => {
let fetchedMvts = []
for(let key in action.donnees.data) {
fetchedMvts.push({...action.donnees.data[key]})
}
let sumIncome = 0;
let sumOutcome = 0;
fetchedMvts.forEach(element => {
let parsed = parseInt(element.amount, 10)
if(element.type === "income") {
sumIncome = sumIncome + parsed;
} else {
sumOutcome = sumOutcome + parsed;
}
})
return {...state, incomes: sumIncome, expenses: sumOutcome}
}
const reducer = (state= initialState, action) => {
console.log('TYPE', action.type)
switch (action.type) {
case actionsTypes.SET_BALANCE: return setBalance(state, action);
default:
console.log('problem')
return state;
}
}
export default reducer;
And, in case you need it, the index.js file of my app :
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'
import { createStore, applyMiddleware} from 'redux'
import * as serviceWorker from './serviceWorker';
import RootReducer from './store/reducers/dashboard'
import thunk from 'redux-thunk'
const store = createStore(RootReducer, applyMiddleware(thunk))
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
Thank you for your help guys
Have a nice day
I'm calling api using redux-thunk. I would like to get the results contained in the action data
if I look at the console and see that the component has dispatched fetchTrending(), the promise is returned.
please help me how to get the results
TrendMovieContainer.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchTrending } from '../actions/index';
import Trending from '../components/Trending';
const TrendMovieContainer = () => {
const title = useSelector(state => state.resluts.title);
//undefined
const dispatch = useDispatch()
const data = dispatch(fetchTrending())
console.log(data); // PromiseĀ {<pending>}
return (
<Trending title={title}/>
)
}
export default TrendMovieContainer;
TrendMovie.jsx
import React from 'react';
const TrendMovie = ({ title, id, img }) => {
return (
<div className="movieBox">
<h3>{title}</h3>
<img src={img} alt={id}/>
</div>
)
}
export default TrendMovie;
action/index.js
import axios from 'axios';
const API_KEY = '224ce27b38a3805ecf6f6c36eb3ba9d0';
const BASE_URL = `https://api.themoviedb.org/3`
export const FETCH_TRENDING = 'FETCH_TRENDING';
export const fetchData = (data) => {
return {
type: FETCH_TRENDING,
data
}
}
export const fetchTrending = () => {
return (dispatch) => {
return axios.get(`${BASE_URL}/trending/all/week?api_key=${API_KEY}&language=en-US`)
.then(response => {
dispatch(fetchData(response.data))
})
.catch(error => {
throw(error);
});
}
}
reducer.js
import { FETCH_TRENDING } from '../actions/index';
export default function reducerTrending(state = [], action) {
switch (action.type) {
case FETCH_TRENDING:
return action.data;
default:
return state;
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import rootReducer from './reducers/index';
import { fetchTrending } from './actions/index';
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk)));
console.log(store.dispatch(fetchTrending()));
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root'));
serviceWorker.unregister();
my store
You don't have to get data from the action. You have to get data from the reducer by using connect function or useSelector hook. As I see, you are trying to use hooks from react-redux library instead of connect function. Then you need to move the dispatching of fetchTrending action to useEffect hook and use useSelector hook to get data from the reducer:
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
...
const TrendMovieContainer = (props) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchTrending());
}, []);
const data = useSelector(state => state.reducerTrending)
console.log(data);
...
}
I want to separate modules, so I tried to separate files in the src/store/modules directory.
To merge reducer modules, I use combineReducers() in modules/index.js.
Before separating these modules, modules/index.js file's code was modules/board.js.
Then I added board.js file. I moved code of index.js to board.js. Finally I added combineReducer() in index.js, but somehow it is not working.
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import store from './store';
const rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
src/containers/BoardContainer.js
import React from 'react';
import Board from '../components/Board';
import { connect } from 'react-redux';
import * as boardActions from '../store/modules/board';
class BoardContainer extends React.Component {
componentWillMount() {
this.props.handleReadBoards();
}
render() {
/* ... */
}
}
const mapStateToProps = (state) => {
return {
boardList: state.get('boardList')
};
}
const mapDispatchToProps = (dispatch) => {
return {
handleReadBoards: () => { dispatch(boardActions.readBoardList()) }
};
}
export default connect(mapStateToProps, mapDispatchToProps)(BoardContainer);
src/store/index.js
// redux
import { createStore, applyMiddleware, compose } from 'redux';
import reducers from './modules';
// redux middleware
import thunk from 'redux-thunk';
const store = createStore(reducers,
compose(applyMiddleware(thunk))
);
export default store;
src/store/modules/index.js
import { combineReducers } from 'redux';
import board from './board';
export default combineReducers({
board
});
src/store/modules/board.js
import { createAction, handleActions } from 'redux-actions';
import { Map, List } from 'immutable';
import * as boardApi from '../../lib/api/board';
// Action Types
const READ_BOARD_LIST = 'board/READ_BOARD_LIST';
// Action Creators
export const readBoardList = () => async (dispatch) => {
try {
const boardList = await boardApi.getBoardList();
dispatch({
type: READ_BOARD_LIST,
payload: boardList
});
} catch (err) {
console.log(err);
}
}
// initial state
const initialState = Map({
boardList: List()
})
// reducer
// export default handleActions({
// [READ_BOARD_LIST]: (state, action) => {
// const boardList = state.get('boardList');
// return state.set('boardList', action.payload.data);
// },
// }, initialState);
// reducer
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case READ_BOARD_LIST:
return state.set('boardList', action.payload.data);
default:
return state;
}
}
Your reducer now contains submodules. So that you have to state from which module you want to get the data: state.board.get('boardList').
You can try to setup redux tool to easy visualize your data inside redux.
const mapStateToProps = (state) => {
return {
boardList: state.board.get('boardList')
};
}
I'm using redux and NetInfo to manager connection detection during startup and any actions where connection is important.
import createOneShotMiddleware from 'redux-middleware-oneshot';
import { NetInfo } from 'react-native';
import { checkConnection } from '../actions/networkActions';
export const middleware = createOneShotMiddleware((dispatch) => {
const handle = (isConnected) => dispatch(checkConnection(isConnected));
NetInfo.isConnected.fetch().done(handle);
NetInfo.isConnected.addEventListener('change', handle);
});
Actions
import * as types from './actionTypes';
export function checkConnection(isConnected) {
return {
type: types.CHECK_CONNECTION,
isConnected: isConnected
};
}
Reducers
import { CHECK_CONNECTION } from '../actions/actionTypes';
const initialState = {
isConnected: false,
};
export default function network(state = initialState, action = {}) {
switch (action.type) {
case CHECK_CONNECTION:
return Object.assign({}, state, {isConnected: action.isConnected})
default:
return state;
}
}
App.js
import React, { Component } from 'react-native';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { middleware as netInfo } from './Middleware/redux-middleware-react-native-netinfo';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger'
import * as reducers from './reducers';
import NavigationScreen from './Containers/NavigationScreen';
const logger = createLogger()
const createStoreWithMiddleware = applyMiddleware(thunk, logger, netInfo)(createStore)
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer)
export default class App extends Component {
render() {
return (
<Provider store={store}>
<NavigationScreen />
</Provider>
);
}
}
It's not work and don't update the state, any suggestions?
This is old but for anyone that arrives here in the future this is how i have done it i tend to do this on my actions creators:
export function networkCheck(){
return (dispatch) => {
const dispatchNetworkState = (isConnected) => dispatch({
type: types.NETWORK_STATE,
state: isConnected
})
const handle = () => NetInfo.isConnected.fetch().done(dispatchNetworkState)
NetInfo.isConnected.addEventListener('change', handle);
}
}
the isue on the code before is that is not following the chain of eventlistener -> testNetwork -> dispatch result