SomeAction is not a function. ( In 'SomeAction()', 'SomeAction' is undefined ).
I get this error when i execute the SomeAction function.
If i only have SomeAction in my actions file and i do
export default SomeAction;
and then import it as below
import SomeAction from 'path/to/action'
it works fine. But since i want more than one functions, i did the following.
this is my Actions.js
const SomeAction = () => dipatch => ({
// Code here
});
const AnotherAction = () => dispatch => ({
// Code here
});
export default { SomeAction, AnotherAction };
then in my App.js
import { SomeAction } from 'path/to/action';
// Here the eslint gives me an error -> 'SomeAction not found in "path/to/action"'
const App = ({ SomeAction }) => {
// Code here
};
App.propTypes = {
SomeAction: PropTypes.func,
}
const mapStateToProps = state => ({
error: state.user.error,
});
export default connect(
mapStateToProps,
{ SomeAction }
)(App);
This worked on a React web app i was coding. Why not in React-Native?
Reviewing, I see you are exporting by default two methods. Normal implementation is one method. Another solution to do this is exporting one by one methods and importing them with their names.
Example of Exporting:
export const SomeAction = () => dipatch => ({
// Code here
});
export const AnotherAction = () => dispatch => ({
// Code here
});
Example of Importing:
import { SomeAction, AnotherAction } from 'path/to/action';
This example is a normal way to export and import functions.
You can not have two default methods exported.
export const SomeAction = () => dipatch => ({
// Code here
};
export const AnotherAction = () => dispatch => ({
// Code here
};
they will be available in your App component in as follow:
import { SomeAction , AnotherAction} from 'path/to/action';
Importing a Defautl export is as follow
import { SomeAction } from 'path/to/action'; or import SomeAction from 'path/to/action';
just use this abpve export const way
Related
The common cause for my issue when researching this is mutating the state and not returning a new object of the state which causes redux to not recognize a change. However, this is not and has never been an issue and i'm well aware of it. I'm returning a new object. In the logger which you can see in the attached image it displays the successful api call resolved and the nextState is updated but never rendered. Refreshing the page acts exactly the same even though i expected to possibly need to do so upon initial landing to root page.
Component:
import pokemonReducer from '../../reducers/pokemon_reducer';
import PokemonIndexItem from './pokemon_index_item';
import {Route} from 'react-router-dom';
import PokemonDetailContainer from './pokemon_detail_container';
class PokemonIndex extends React.Component {
componentDidMount() {
this.props.requestAllPokemon();
}
render() {
const pokemon = this.props.pokemon;
return (
<section className="pokedex">
<Route path='/pokemon/:pokemonID' component={PokemonDetailContainer} />
<ul>{pokemon && pokemon.map(poke => <li>{poke.name}{poke.id}</li>)}</ul>
</section>
);
}
}
export default PokemonIndex;
and the container:
import {connect} from 'react-redux';
import { selectAllPokemon } from '../../reducers/selectors';
import PokemonIndex from './pokemon_index';
import { requestAllPokemon } from '../../actions/pokemon_actions';
const mapStateToProps = state => ({
pokemon: selectAllPokemon(state)
});
const mapDispatchToProps = dispatch => ({
requestAllPokemon: () => dispatch(requestAllPokemon())
});
export default connect(mapStateToProps, mapDispatchToProps)(PokemonIndex);
the reducer:
import { RECEIVE_ALL_POKEMON, RECEIVE_SINGLE_POKEMON} from '../actions/pokemon_actions';
const pokemonReducer = (initialState = {}, action) => {
Object.freeze(initialState);
switch(action.type) {
case RECEIVE_ALL_POKEMON:
return Object.assign({}, initialState, action.pokemon);
case RECEIVE_SINGLE_POKEMON:
let poke = action.payload.pokemon
return Object.assign({}, initialState, {[poke.id]: poke})
default:
return initialState;
}
};
export default pokemonReducer;
secondary reducer:
import { combineReducers } from 'redux';
import pokemonReducer from './pokemon_reducer'
const entitiesReducer = combineReducers({
pokemon: pokemonReducer,
});
export default entitiesReducer;
rootreducer:
import {combineReducers} from 'redux';
import entitiesReducer from './entities_reducer';
const rootReducer = combineReducers({
entities: entitiesReducer
});
export default rootReducer;
as requested here is the selectors defined in reducers folder
export const selectAllPokemon = (state) => {
Object.values(state.entities.pokemon);
};
export const selectSinglePokemon = (state) => {
Object.values(state.entities.pokemon)
};
and here is the actions created:
export const RECEIVE_ALL_POKEMON = "RECEIVE_ALL_POKEMON";
export const RECEIVE_SINGLE_POKEMON = "RECEIVE_SINGLE_POKEMON";
import * as APIUtil from '../util/api_util';
export const receiveAllPokemon = (pokemon) => (
{
type: RECEIVE_ALL_POKEMON,
pokemon
}
);
export const requestAllPokemon = () => (dispatch) => {
APIUtil.fetchAllPokemon()
.then(
pokemon =>
{ dispatch(receiveAllPokemon(pokemon));}
);
};
export const receiveSinglePokemon = data => (
{
type: RECEIVE_SINGLE_POKEMON,
data
}
);
export const requestSinglePokemon = id => (dispatch) => {
APIUtil.fetchSinglePokemon(id)
.then(pokemon => {dispatch(receiveSinglePokemon(pokemon));
return pokemon;});
};
nextstate showing in console
As you stated in your question, your redux state is getting properly set but your new state is never being rendered and I think this has to do with your selector. It looks to me that you forgot to return your computed state.
export const selectAllPokemon = (state) => {
Object.values(state.entities.pokemon);
};
// will return undefined
For returning your state you have two options:
Explicit return
export const selectAllPokemon = (state) => {
return Object.values(state.entities.pokemon);
};
Implicit return
export const selectAllPokemon = (state) => (
Object.values(state.entities.pokemon);
);
I refer to this article or look at the examples I created in playground to get a better unstanding of implicit and explicit return in arrow functions.
I'm trying to dispatch an action which is by using createStandardAction(typesafe-actions) and then it goes to epic(redux-observable) for api call.
The strange part is that it works perfectly with stub data, it completes the flow(i.e., component->action->epic->reducer->store) but the action doesn't trigger or enters the epic while using it with the actual server
**Component:-**
export const mapDispatchToProps = (dispatch: Dispatch): ReduxActions => ({
loadTestData: () => dispatch(loadTestData())
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(withNavigation(loadData))
**Action**
import { ActionsUnion, createStandardAction } from 'typesafe-actions'
export const LOADDATA_GET = 'LOADDATA_GET'
export const loadData = createStandardAction(LOADDATA_GET)<void>()
const actions = {
loadData
}
export type AllActions = ActionsUnion<typeof actions>
**Epic**
import { Action, MiddlewareAPI } from 'redux'
import { ActionsObservable, Epic } from 'redux-observable'
import { Observable } from 'rxjs'
import {
LOADDATA_GET
} from './loadData.actions'
export const getloadDataEpic: Epic<Action, ReduxState> = (
action$: ActionsObservable<any>,
store: MiddlewareAPI<any, ReduxState>,
{ mobileAPI }: EpicDependencies
) =>
action$
.ofType(LOADDATA_GET)
.mergeMap((action) => {
return Observable.merge(
mobileAPI
.getJSON('/dummypath/loadData')
.mergeMap((response) => {
return Observable.of<any>(
setLoadData(response)
)
})
)}
)
.catch((error) => {
return Observable.of(errorAction(error))
})
I am really confused why the flow doesn't comes to epic for the actual server while for local json data and dummy path it works
Issue fixed, there was some data-mapping issue on the server side
I'm using react-navigation and calling that service in my code. Although I'm not sure how to mock the navigate function. Here is my code:
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
Here is what I got so far:
export const loginEpic = (action$, state$, { ajax, navigate }) =>
action$.pipe(
ofType(LOGIN_REQUEST),
map(() => state$.value),
switchMap((options) =>
ajax(options).pipe(
pluck("response"),
map((res) => loginSuccess(res)),
tap((r) => navigate(ROUTES.DASHBOARD_SCREEN))
)
)
);
navigate is navigationService.navigate and I'm passing it from the dependencies of redux-observables.
The test looks like this:
const dependencies = {
ajax: ({ }) => of(mockedResponseFromAjax),
navigate: () => // ???
};
const result$ = loginEpic(action$, state$, dependencies).pipe(toArray());
Option 1: - What worked for me:
import NavigationService from '<your-path>/NavigationService';
beforeEach(() => {
NavigationService.navigate = jest.fn();
});
within my test file.
Option 2: - Better Version
see https://stackoverflow.com/questions/55319581/why-isnĀ“t-mock-from-mock-folder-invoked-when-running-test-for-redux-action
it is important that you activate the mock by jest.mock('../../app/utils/NavigationService'); this has to be placed at the top of your test file directly behind the imports
i`m learning redux and after setting all the setup i get this message:
Error: Actions must be plain objects. Use custom middleware for async actions.
I readed 10 different issues with this mistake but not single solution works for me. here is my project on github, in case problem not in following code:https://github.com/CodeNinja1395/Test-task-for-inCode/tree/redux
store:
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
export default store;
actions:
import {FETCH_DATA} from './types';
export const fetchData = () => dispatch => {
fetch('https://raw.githubusercontent.com/CodeNinja1395/Test-task-for-inCode/master/clients.json')
.then(posts =>
dispatch({
type: FETCH_DATA,
payload: posts
})
);
};
You use an old version of redux - v1, while the new version is v4. When you upgrade to v4 it works.
npm install redux#^4.0.0
You're not returning your fetch() call. You have to make sure you return your fetch call inside of the action creator.
export function fetchData() {
return function(dispatch) {
return fetch( ... ).then( ... ).catch( ... )
}
}
export const fetchData = (params) => dispatch => {
dispatch(beginAjaxCall());
return fetch(...).then(response => {
dispatch(someFunction(response));
}).catch(error => {
throw(error);
});
}
export const loadIncidents = (params) => dispatch => {
dispatch(beginAjaxCall());
return IncidentsApi.getIncidents(params).then(incidents => {
dispatch(loadIncidentsSuccess(incidents));
}).catch(error => {
throw(error);
});
}
The top is ES5, middle ES6, and third is an ES6 version of a function that I used in a project.
Hope that helps.
I'm using a router component for routing my app. I have a file like this:
import CustomerDetailsContainer from '../views/CustomerDetails/customerDetailsContainer';
import CustomerInfoContainer from '../views/CustomerInfo/customerInfoContainer';
import { setUIState } from '../../actions/index';
const inCustomerInfo = (store) => {
store.dispatch(setUIState(CURRENT_SCREEN, 'customerInfo'));
};
const inCustomerDetails = (store) => {
store.dispatch(setUIState(CURRENT_SCREEN, 'customerDetails'));
};
export default (store) => {
return [
authenticatedRouteConfig(
store,
`/${CUSTOMER_INFO}`,
CustomerInfoContainer,
inCustomerInfo
),
authenticatedRouteConfig(
store,
`/${CUSTOMER_DETAILS}/:cid`,
CustomerDetailsContainer,
inCustomerDetails
),
];
};
And error is showing that store.dispatch is not a function. What am i missing? Why this message is appearing? Isn't store a global variable?
You have to use dispatch from redux to dispatch action.
import {connect} from "react-redux"
const mapDispatchToProps = (dispatch) => {
return {
inCustomerInfo : (setUIState) => {
dispatch(setUIState(CURRENT_SCREEN, 'customerInfo')
},
inCustomerDetails : (setUIState) => {
dispatch(setUIState(CURRENT_SCREEN, 'customerDetails')
}
}
export default connect(mapDispatchToProps)(Comp)