How to arrange action creators in a react-redux application? - reactjs

I'm fairly new to React & Redux and all this while, I'm putting all my action creators & my constants into a single file '../actions/index.js'.
When I started out, this seemed fine but now, it's become this huge block of unreadable code.
Would you suggest breaking down index.js into functionality1.js, func2.js, func3.js and then importing actions as needed from split up files:
import {sampleAction} from '../action/func1';
Or is there a better way to do this?
Sorry if this seems like a silly question. I'm still trying to understand best practices in a React-Redux application.

You can still split it up but retain the index.js for simplicity using the export * from '...' syntax.
actions/functionality1.js:
export const ACTION_1 = '...'
export const ACTION_2 = '...'
export const action1 = () => {...}
export const action2 = () => {...}
actions/functionality2.js:
export const ACTION_3 = '...'
export const ACTION_4 = '...'
export const action3 = () => {...}
export const action4 = () => {...}
actions/index.js:
export * from './functionality1'
export * from './functionality2'
Then you can import any you need like so:
import { action1, action4, ACTION_2, ACTION_3 } from './actions'

Related

How to Access action creators names with auto-complete in a react component?

I am using react and redux with typescript lately and it's awesome.
I can access my store state inside useAppSelector( as specified in react-redux official document ) with auto-complete option which speeds-up development time, but I can't access my action creators names with auto-complete inside my useAppDispatch hook.
The reason is I am using redux-thunk middleware in my App, so I have to use ThunkDispatch to type AppDispatch, which has an AnyAction type for action.
//react-redux document
import {ThunkDispatch} from 'redux-thunk';
import {AnyAction} from 'react-redux';
type RootState = ReturnType<typeof store.getState>;
type AppDispatch = ThunkDispatch<RootState, any, AnyAction>;
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
Currently I export an object which contains all my action creators, and importing it inside any component gives me their names.
like This:
// inside actions.ts
const AppActions = {
action1: () => {},
action2: () => {},
...
}
export default AppActions;
// inside index.tsx
import AppActions from 'actions.ts'
// here i can access them like
AppActions.action1()
So is there a better way to do this ?
Hey there so maybe can you post your store ts file ? then
you will export AppDispatch = typeof store.dispatch;
then in your hooks for
export const useAppDispatch = () => useDispatch<AppDispatch>()
Then use it in your app like
const dispatch = useAppDispatch()
dispatch(YOU SHOULD SEE YOUR STORE ACTIONS OPTIONS)

Get list of all exported components in file

I have a lot of minor components:
export const A = () => {...}
export const B = () => {...}
...
export default [A, B, ...];
After adding another component to the file, I might forget to add it to the export default [...]. By now you should see what the problem is.
How should I resolve this issue? Is it maybe possible to define a list that includes all exported functions?
I want to be able to iterate over all components in other files. Hence why it has to be a list they are grouped into.
origin file src/foo.js:
export const A = () => { ... };
export const B = () => { ... };
export const C = () => { ... };
Create an src/index.js as following:
import foos from './foo';
const allComponents = Object.values(foos);
export default allComponents;
From another file we'd:
import foos from 'src/foo';
foos[0]() // A()
foos[1]() // B()
...
EDIT: fixed the example code, needs to use values() not keys(), my bad
With some minor modifications, you can use import *. This gives you all of the named exports of a file, wrapped up into an object. If, for example, your old code was doing this:
import allComponents from 'someFile'
allComponents.forEach(component => {
// do something with the component
})
... instead do this:
import * as allComponents from 'someFile'
Object.values(allComponents).forEach(component => {
// do something with the component
})
As new exports are added to the file, this import statement will pull them in automatically, and so the default export array shouldn't be needed anymore.

React redux - actions

Recently I'm trying my best in redux, and I have seen a really good folder structure project, I tried to get the same structure, but the same way didn't work...
E.g. I've got a path something like that: ./src/_actions and inside this folder I've got "user.actions.js", "alert.actions.js", "index.js".
In alert.actions.js I've got something like that:
import { alertConstants } from "../_constants/alert.constants";
export const alertActions = {
success,
error,
clear,
};
function success(message) {
return { type: alertConstants.SUCCESS, message };
}
function error(message) {
return { type: alertConstants.ERROR, message };
}
function clear() {
return { type: alertConstants.CLEAR };
}
And I'd love to import all of them from one place like to the folder where path is "./../test.js":
import {alertActions} from "./../_actions";
import {useDispatch} from "react-redux";
export const test = () => {
const dispatch = useDispatch();
return (
<button onClick={() => dispatch(alertActions.success("test"))}> Click </button>
)
}
but I got something like "alertActions.success" is undefined. I don't know what I'm doing wrong.. In that project, index.js has been empty as well... that object supposed to export those all functions.. somebody know any solution? :(
You need to export the object after the functions are made. Usually they are hoisted, but in this case you are probably using strict mode and they are not. You have to either move the export object or better yet export all of them individually and then when you are importing them you should write:
import * as alertActions from 'youfile'
And if you want to export a whole object you should export it like:
export default alertActions
And then you need to import them like:
import alertActions from 'yourfile'
and access them:
alrtActions.error()

Redux Observable Action with payload types don't match. But without payload its working

The code is as follows
import { Action } from "redux";
import { ActionsObservable, ofType, combineEpics } from 'redux-observable'
import { map } from 'rxjs/operators'
export enum ActionTypes {
One = 'ACTION_ONE',
Two = 'ACTION_TWO'
}
export interface One extends Action {
type: ActionTypes.One
//commnent out next line to remove error
myStr: string
}
export const doOne = (myStr: string): One => ({
type: ActionTypes.One,
//comment out next line to remove error
myStr
})
export interface Two extends Action {
type: ActionTypes.Two
myBool: boolean
}
export const doTwo = (myBool: boolean): Two => ({ type: ActionTypes.Two, myBool })
export type Actions = One | Two
export const epic = (action$: ActionsObservable<Actions>) =>
action$.pipe(
ofType<Actions, One>(ActionTypes.One),
map((action: any) => ({
type: ActionTypes.Two,
myBool: true
}))
)
export const rootEpic = combineEpics(epic)
Also how I generate the store
import { createEpicMiddleware } from "redux-observable"
import { createStore, applyMiddleware } from "redux"
import { reducer } from "./reducer"
import { rootEpic } from "./epic"
export const configStore = () => {
const epicMiddleware = createEpicMiddleware()
const store = createStore(reducer, applyMiddleware(epicMiddleware))
epicMiddleware.run(rootEpic)
return store
}
I have spent last 4 hours trying to add payload to action but with every possible filter solution it fails. I tried asking for help on redux observable github section and their chat room. I also googled for simple example that is strictly typed. Didn't find any solution.
I had the same issue than you, my solution was:
As you stricly typed your epic:
export const epic = (action$: ActionsObservable<Actions>)
// Not the subject, but I would prefer
export const epic: Epic<Actions> = action$ =>
You have to stricly type your epicMiddleware too:
const epicMiddleware = createEpicMiddleware<Actions>()
With this, your epicMiddleware.run method will attempt Actions instead of Action<any> and it should work.
just follow typesafe-action github example. This repository really care about being strictly typed. Redux observable people only care if it works with javascript. And if it doesn't work with strictly typed typescript then they simply ignore the problem. And ignore any request for help. Luckily typesafe-action example was built with redux observable so it worked for me.

Why not dispatch directly into store from action creator redux?

I am building an app using redux and react-native.
I am curious about a pattern which I use. I have encountered no downsides however I haven't seen it in any tutorials which makes me wonder why nobody does it.
Instead of passing action creators as props in the connect function like
connect(mapStateToProps,{ func1, func2 })(Component);
I imported the app store inside of the module where I declare the functions in the first place:
import { AppStore } from '../App';
const actionCreator = () => {
doSomethng();
appStore.dispatch({ type: 'Action' });
};
This to me makes it easier to do async actions because I need no middleware:
import { AppStore } from '../App';
const actionCreator = async () => {
await doSomethng();
appStore.dispatch({ type: 'Action' });
};
I did this because of the js-lint error 'no-shadow'. It made me realise that in order to use it I had to import the action creators in the component file, and then pass it as a prop to the connect function in order for the action creator to have access to dispatch.
import { actionCreator1, actionCreator2 } from './actionCreators';
const myComponent = (props) => {
const { actionCreator1, actionCreator2 } = props; //shadowed names
return (
<Button onPress={actionCreator1} />
);
};
export default connect({}, { actionCreator1, actionCreator2 })(myComponent)
In my version I just import it once but do not pass it to connect. This eliminates the need to shadow names.
import { actionCreator1, actionCreator2 } from './actionCreators';
const myComponent = (props) => {
return (
<Button onPress={actionCreator1} />
);
};
export default connect({})(myComponent)
I like that you try to find your own solutions to your specific problems. It's the sign of an engineer, just in this case this isn't the solution.
I think the idea of how Redux teaches you to do things is not intended to be canonized. You have the ability to put a dispatcher on your props because it allows things to be transparent, meaning that things are bound outside of your class and injected in. You have hidden your store dependency by directly referencing it in some other files. It's no longer as obvious how your application works with regards to the workflow. Another react developer would be confused, I suppose that's the major downside.
If you're ok with those aspects what you're doing is fine. Fine as in, it gets the job done, but not "fine" in that it embraces concepts like Single Responsibility Principle

Resources