redux-utilities / redux-actions Goto Github PK
View Code? Open in Web Editor NEWFlux Standard Action utilities for Redux.
Home Page: https://redux-actions.js.org
License: MIT License
Flux Standard Action utilities for Redux.
Home Page: https://redux-actions.js.org
License: MIT License
Would be useful to accept a matcher to match our action before reducing the state.
Something like
export default function handleMatchingAction(matcher, reducers) {
return (state, action) => {
// If action does not match, return previous state
if (!matcher(action)) return state;
const handlerKey = isError(action) ? 'throw' : 'next';
// If function is passed instead of map, use as reducer
if (isFunction(reducers)) {
reducers.next = reducers;
}
// Otherwise, assume an action map was passed
const reducer = reducers[handlerKey];
return isFunction(reducer)
? reducer(state, action)
: state;
};
}
export default function handleAction(type, reducers) {
return handleMatchingAction(action => action.type === type, reducers)
}
npm package redux-actions contains old code, for ex. it hasn't "Handle FSA errors where the payload is an Error object. Set error." in createAction.js could you please update npm package. Thanks.
I want to use enum for defining action types,such as
enum ActionTypes {
ADD,
DELETE
}
const add = createAction(ActionTypes.ADD, item => item)
const reducer = handleActions({
[ActionTypes.ADD](state, item) {
return [...state, item]
}
}, initialState)
After compile, ActionTypes.ADD
will transform to number 0
.
But the key of handlers will become string '0'.
Because '0' !== 0
, the demo above will not work.
In the project I am working on, I find that timestamp is needed so often that it makes sense to add timestamp (when the createAction function was executed) as default for action.meta. If you think it's a good idea too I can make this happen via pull request.
ie.
if (typeof metaCreator === 'function') {
action.meta = metaCreator(...args);
} else {
action.meta = {
timestamp: Date.now()
}
}
I'm using redux-actions with redux-promise. When my promise succeeds, it hits my handleActions
reducer as expected, but when it fails, nothing happens. The action is fired off but my handler never sees the failed FSA.
From devtools I see that the FSA has error
property set to true
as expected, but my reducer below does not get called.
const reducers = handleActions({
[FOO_ACTION]: (state, action) => {
console.log(action); // when promise fails, I don't see this!
return state;
},
}, initialState);
Then I tried something like this but it didn't work. I'm pretty sure this is not how we're supposed to use handleActions
.
const reducers = handleActions({
[FOO_ACTION]: handleAction(FOO_ACTION, {
next: (state, action) => {
console.log('woo hoo:', action);
return state;
},
throw: (state, action) => {
console.log('aww man:', action);
return state;
},
}),
}, initialState);
Could someone shed some more light on how to handle failed promises?
const favoriteEnd = createAction(types.FAVORITE_END, (id) => ({ id }))
favoriteEnd(15) // payload.id === 15
favoriteEnd(new Error('foo')) // payload.id === error
while I'd expect
favoriteEnd(new Error('foo')) // payload === error
Am I missing something ?
This could be a really bad idea, but I thought I'd run it by you. I often find that I'm just wanting to write a quick reducer that just moves the action's payload to the state. Being able to just type:
const reducer = handleAction(ACTION_TYPE, initialState);
instead of
const reducer = handleAction(ACTION_TYPE, (_, {payload}) => payload, initialState);
would be really nice. Just a thought.
handleActions
doesn't correctly match actions with types that are Symbol
s, e.g.
const TEST_ACTION = Symbol('TEST_ACTION')
const OTHER_TEST_ACTION = 'OTHER_TEST_ACTION'
const reducer = handleActions({
[TEST_ACTION]: () => 'foo',
[OTHER_TEST_ACTION]: () => 'bar',
}, 'baz')
let state = 'test'
state = reducer(state, {type: TEST_ACTION})
console.log(state) // test (unexpected)
state = reducer(state, {type: OTHER_TEST_ACTION})
console.log(state) // bar (fine)
TEST_ACTION
is broken, OTHER_TEST_ACTION
is fine.
There is a .babelrc published on npm, which is configured for babel5. It will cause error on babel 6, like react-native >=0.16.0
Is it ok to add it into '.npmignore' and does not publish it on npm?
We want to be able to support handlers in the form of:
{
begin: (state, action) => {},
end: {
next: (state, action) => {},
throw: (state, action) => {},
}
}
In order to do so, we have to inspect themeta
field of the action and switch on the async
field there. We also have to make sure that the handlers receive all arguments the original action receives, so that we can do optimistic updates easily.
In case an async handler receives a non-async action it should just handle it the same way it would handle the end
status of an async action.
Is there a way to get state in createAction?
Is it wrong approach if I need to get data from state inside action creator?
To prevent no-reserved-keys
eslint rule error (which is on by default at a lot of companies)
Every time i need to write in handleAction(s) something like this
// eslint-disable-line no-reserved-keys
or this
...
['throw'](state, { payload }) { ...
...
may be it will be more likely to give an alias for throw like error
, onError
etc...
I already made a note of this in #43, but I wanted to promote it to its own issue.
I personally think that if you pass an error in as the payload, it should bypass your custom payloadCreator logic and just set the payload equal to the passed-in error. Otherwise, you have to add an error check in each of your action creators to achieve the same effect.
Example:
const createCoolAction = createAction(COOL_ACTION_TYPE, spec => {
const {zipCode, id} = spec;
return {body: {zipCode, id}};
});
So, if I do something like, createCoolAction(new Error('Something bad happened'))
, the payload will be set to {body: {zipCode: undefined, id: undefined}}
instead of just the error object itself. To fix this, I could add an error check, but I don't want to have to do this in every one of my action creators.
I'd be happy to make an MR for this.
When creating an FSA-compliant action that represents an error (error
property set to true
), how can I do that? Sorry if it's an obvious answer.
Since we have to return a non-undefined state in redux, we should probably make the defaultState
parameter for handleActions
required. Thoughts?
I already checked out createAction file for more information, but it seems don't have any mechanisms to handle error with FSA error action in createAction
function. It can't mutate any values outside of payload.
But I thought that it's a common case to handle error in a actionCreator, like below:
// With babel - optional: ["runtime"]
export const search = createAction(SEARCH_USER, async username => {
try {
const data = await getGithubInfo(username);
return data;
} catch (err) {
// handle error here
}
});
It doesn’t make sense to abort error handling support here.
Any ideas?
I'm trying to write payloadCreator
and metaCreator
for an action, when this action been called, a promise is passed in, now both of two creators try to resolve the promise, but promise as we know, only can be resolved one time, so I'm confused: how metaCreator works exactly?
Hi,
i have this issue:
Error while persisting cache: TransformError:
/Users/saro/Projects/openshift/reactNative/node_modules/redux-actions/lib/index.js:
[BABEL] /Users/saro/Projects/openshift/reactNative/node_modules/redux-actions/lib/index.js:
Using removed Babel 5 option:
/Users/saro/Projects/openshift/reactNative/node_modules/redux-actions/.babelrc.stage -
Check out the corresponding stage-x presets http://babeljs.io/docs/plugins/#presets
Any suggestions?
If I wrap a reducer in handleAction
I get this error:
"Reducer "selectedReddit" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined."
If I debug I see redux tries to get a initial state by calling the wrapped reducer with undefined state and a specific system action type. Because the type does not match the wrapped one the inner reducer never runs and no state default is created.
If I use handleActions
(m) with default state it works fine. Same function with default state in arguments and classic action-type check is also fine.
Anyway,is handleAction
usable like this? Readme doesn't really specifiy use case.
With this line (see below for code) being added to createAction
, should we use toString
on the action creator to reference actions instead of creating constants?
export default function createAction(type, actionCreator, metaCreator) {
// code removed for brevity
const actionHandler = (...args) => {
// removed for brevity
return action;
};
// this line is what I'm referring to
actionHandler.toString = () => type;
return actionHandler;
}
Basically, is there any reason I should be creating and exporting constants for action types, or should I just export action creators, and code that needs to reference the type can just use myActionCreator.toString()
?
As title, how to set meta property?
I started reading the docs for flux standard actions, and they make a lot of references to flux. I'm just wondering if it will hinder my understanding of redux-actions if I don't go learn Flux, which I would rather not do.
The createAction
function returns an action creator with the signature function(...args)
. However, if you do not pass an actionCreator
method to the createAction
function, then the "identity
" function is used.
The identity
function is this: (t) => t
, which does not do an argument spread and, since it is called with function.apply, it only handles the first argument passed in to the action creator, even though the action creator is designed around supporting n arguments.
Since handleActions
returns a reducer, a proper name for it would be createReducer
. I've tried to teach redux to a friend and it wasn't immediately apparent that handleActions
returns a reducer. What do you think? I'm not adamant on createReducer
, as it doesn't differentiate from handleAction
. I just think that handleActions
might not be the proper name for it.
Would have saved me the last 20 mins :)
What do you think about adding a GUID field to each action to allow unique identification. This could be useful for:
You could put it into the meta property, but I think this could prove useful as a first class concept.
Hi! I am new to React and redux. And would appericate some help or direction where to go next. Here few questions I have:
I tried using
const INCREMENT = 'logos/counter/INCREMENT'
const RESET = 'logos/counter/RESET'
export const counterReducer = handleActions({
INCREMENT: (state, action) => {
return state + action.payload
},
RESET: (state, action) => ({
counter: 0,
}),
}, 0)
and the INCREMENT part was never called
however
export const counterReducer = (state=0, action) => {
switch(action.type) {
case INCREMENT:
return state + action.payload
case RESET:
return 0
default:
return state
}
}
worked just fine
Currently to get a default state from a createActions-returned reducer you must use:
const store = Redux.createStore(
reducer,
reducer(undefined, {}),
Redux.applyMiddleware(logger)
)
If the reducer returned by createActions defaulted the action to {}, we can now do:
const store = Redux.createStore(
reducer,
reducer(), // yay! no more action.type errors!
Redux.applyMiddleware(logger)
)
import createAction from 'redux-actions';
or
import { createAction } from 'redux-actions';
??
Why is this missing in the readme?
Consider this code, it's commonly used in my project:
import * as actionTypes from './actionTypes.js'
export default handleActions({
[actionTypes.ACT_1]: (state, action) => {},
[actionTypes.ACT_2]: (state, action) => {},
})
If there's a typo in actionTypes variable(ACE_1
for instance), there's no warning, linters can't tell the error too. But it's easy to print warning message using redux-actions.
I'd like to send a PR if it's okay to you.
https://github.com/zenparsing/es-observable#observer
interface Observer {
// Recieves the next value in the sequence
next(value);
// Recieves the sequence error
error(errorValue);
// Receives the sequence completion value
complete(completeValue);
}
handleAction()
/ handleActions()
should use the same naming scheme:
const reducer = handleAction(ACITON_TYPE, {
// Receives normal actions
next(state, action);
// Receives `error: true` actions
error(state, action);
// Receives `meta.sequence: 'complete'` actions
complete(state, action);
});
handleAction use flux-standard-action method isError
import { isError } from 'flux-standard-action';
the method is simple, but it will import some lodash method when compile by webpack.
function isError(action) {
return action.error === true;
}
I suggest remove the flux dep for descrease the compilse file size.
Hi, I just started toying around with this lib so I'm probably missing something important. The spec for FSA states that an action MAY have a payload
property, however when I do this:
const type = 'TYPE';
const myActionCreator = createAction(type);
const action = myActionCreator();
console.log(action); // <- {type: "TYPE", payload: undefined} but I expected {type: "TYPE"}
Is there a reason for this?
I'd like to be able to handle the fact that some async action has started, and have state reflex that.
Current handleActions(reducerMap, ?defaultState)
signature doesn't allow the propagation of payload types from actions into reducers, as it is a dictionary type.
A builder or factory pattern like the following would allow for better type propagation
let increment = createAction<void>('INCREMENT');
let decrement = createAction<number>('DECREMENT');
ReducerBuilder()
.addReducer(increment, (state, action) => {})
.addReducer(decrement, (state, action) => {})
.toReducerMap();
The reason being the actions could be typed as Action<void>
and Action<number>
respectively, and .addReducer
can be typed as
function addReducer<TState, TPayload>(action: Action<TPayload>, handler: (state: TState, action: Action<TPayload>)=>{});
This would automated propagate the type into the reducer.
Just want to discuss this, since was confused by default actionCreator factory behaviour. As per docs if I define my action crator like that:
export const toggleBaseline = makeActionCreator('Dbg.TOGGLE_BASELINE')
identity function will be used for payload. I assumed that it's fine to create Action Creators that don't have a payload in this manner and it was wrong assumption. Read below why.
After creating it I passed it to my component like that:
export default connect(state => state.debug, {
toggleBaseline,
})(DebugGrid)
Then inside DebugGrid
component I just hooked it to onClick
handler:
<input type="checkbox" onChange={props.toggleBaseline}
All seemed fine, do you see a problem at this point? I didn't see any =) And if you don't see it also then we have a problem and need to do something about it. This is main point of this issue.
Then I opened Redux Dev Tools, clicked on my checkbox and saw this:
That was a moment of "whait a minute..." aha, right since Action Creator uses identity function and I pass it "as-is" to reduce boilerplate to my component event handler it grabs React's synthetic event to it's params, but why it becomes so slow, 400ms for an action dispatch?
Then I realized that default settings in Redux Dev Tools is to serialize state and that synthetic event object must be huge so serialization of it takes 300-400ms.
If I haven't used Dev Tools I would have not discovered this untill much later when I would be out of context and probably would spend days to figure this out.
So then I figured that default "safe" way to define Action Creators is like this:
makeActionCreator('Dbg.TOGGLE_BASELINE', () => {})
It looks a little weird to me, am I missing something? Thanks!
Hi all!
I'd like to propose a new feature to allow handling multiple actions with the same reducer in the case that someone has very general, over-arching properties of their state.
For example, when using redux on its own I've found it useful to have a loading
property which gets reduced like this:
function loading(state = false, action) {
switch(action.type) {
case LOAD_SOMETHING_START:
case LOAD_SOMETHING_ELSE_START:
return true;
case LOAD_SOMETHING_COMPLETE:
case LOAD_SOMETHING_ELSE_COMPLETE:
return false;
default:
return state;
}
}
I find this preferable to invoking an action to update loading
within a composite/async action using redux-thunk because I would prefer not to have my state's other properties know about the loading
property and keep that logic central to the reducer for loading
.
Initially I'd like to propose being able to define an action handler with a name that contains a delimiter like |
. To illustrate with the previous example:
handleActions({
'LOAD_SOMETHING_START | LOAD_SOMETHING_ELSE_START': () => true,
'LOAD_SOMETHING_COMPLETE | LOAD_SOMETHING_ELSE_COMPLETE': () => false
}, false);
Alternatively, if it would be preferable not to expose the details about how to delimit action names, a function combineActions()
might help:
const startActions = combineActions('LOAD_SOMETHING_START', 'LOAD_SOMETHING_ELSE_START');
const completeActions = combineActions('LOAD_SOMETHING_COMPLETE', 'LOAD_SOMETHING_ELSE_COMPLETE');
handleActions({
[startActions]: () => true,
[completeActions]: () => false
}, false);
What do you think? I'd be thrilled to submit a PR for this if we agree it would be worthwhile.
export const FAILED_REQUEST_ORDER =
${GET_FUNCTIONALITY}_FAILED
;
export const failedOrder = createAction(FAILED_REQUEST_ORDER);// in calling code
// ...
.catch(error) {
const errorMeta = {
'endpoint': endpoint,
'options': options
};
dispatch(failureAction(error, errorMeta));
}
I would like to dispatch an error object as a payload, but also have meta data information. I can hack around it, but I think it makes sense for createAction to provide a means to set meta separately from payload. This is important for error handling. Thanks for your time.
-- currently I can't perform the code above, even though I think it should be able to do so) -- a hack exists where I do this:
.catch((error) => { const errorMeta = { 'endpoint': endpoint, 'options': options }; let failureActionObject = failureAction(error); failureActionObject.meta = errorMeta; dispatch(failureActionObject); });
I'm using combineReducers and i'm wondering how I can use handleActions to have two different store keys be updated by the one action type. My requirement is that the one action type be used to update different keys inside the store, for example I have a transactions array which must add a new transaction then I must update the balance of the account the transaction relates to.
This doesn't seem to work, or am I doing this wrong?
import { combineReducers } from 'redux';
import { handleActions } from 'redux-actions';
const ADD_TRANSACTION = 'ADD_TRANSACTION';
let defaultTransactionsState = [];
let defaultAccountsState = [];
// add transaction to transactions state
let transactions = handleActions({
[ADD_TRANSACTION]: (state, action) => ([action.payload, state...])
}, defaultTransactionsState);
let accounts = handleActions({
[ADD_TRANSACTION]: (state, action) => {
// update the balance of account based on transaction being added
let payload = action.payload;
let accountToEdit = state.find((account) => account.id === payload.accountId);
let newBalance = accountToEdit.balance + (payload.inAmount - payload.outAmount);
return state.map(account =>
account.id === accountToEdit.id
? {...account, balance: newBalance}
: account
);
}
}, defaultAccountsState);
combineReducers({
transactions,
accounts
});
I am wondering why we are required to merge in the current state in every reducer function.
const initialState = {
selectedTagIds: [],
tags: getTagsForLanguage('en')
}
const reducer = handleActions({
[ADD_FILTER_TAG]: (state, { payload }) => ({ ...state, selectedTagIds: [...state.selectedTagIds, payload] }),
[REMOVE_FILTER_TAG]: (state, { payload }) => ({ ...state, selectedTagIds: state.selectedTagIds.filter(id => id !== payload) }),
[CLEAR_FILTER]: (state) => ({ ...state, selectedTagIds: [] }),
}, initialState)
If handleActions was something like...
function handleActions(actionsMap, initialState) {
return (state = initialState, action) => {
const reduceFn = actionsMap[action.type]
if (reduceFn) {
return {...state, ...reduceFn(state, action)}
}
return state
}
}
... you could write
const reducer = handleActions({
[ADD_FILTER_TAG]: (state, { payload }) => ({ selectedTagIds: [...state.selectedTagIds, payload] }),
[REMOVE_FILTER_TAG]: (state, { payload }) => ({ selectedTagIds: state.selectedTagIds.filter(id => id !== payload) }),
[CLEAR_FILTER]: (state) => ({ selectedTagIds: [] }),
}, initialState)
It makes it less verbose and harder to mess up imho
Say I've got a basket which you can add items to or remove items from. If I add an item which already exists in the basket, I update the quantity property instead of adding a new entry in the basket. I use the reducer composition pattern to delegate the responsibility of updating the quantity to a separate function which handles properties within the item. Here's the code:
const basketItem = (state, action) => {
switch (action.type) {
case ADD_TO_BASKET:
if (state.id !== action.payload.id) {
return state;
}
return Object.assign({}, state, {
quantity: state.quantity + action.payload.quantity
});
default:
return state;
}
}
let basket = handleActions({
ADD_TO_BASKET: (state, action) => {
let itemIndex = state.findIndex(item => {
return item.id === action.payload.id;
})
if (itemIndex === -1) {
//if the item doesn't exist in the basket already return the new item with the existing array
return [...state, action.payload];
} else {
return state.map(i => basketItem(i, action));
}
},
REMOVE_FROM_BASKET: (state, action) => {
let index = state.findIndex(item => {
return item.id === action.item.id
})
return [...state.slice(0, index), ...state.slice(index + 1)];
}
}, []);
So my questions are:
Thanks
Just as there's a handleActions
, perhaps there should be a createActions
as sugar for creating multiple actions?
createActions(
{
[type1]: payload1Creator,
[type2]: {
"payload": payload2Creator,
"meta": meta2Creator,
}
}
);
This seems a bit nicer (and less redundant) than
{
[type1]: createAction(type1, payload1Creator),
[type2]: createAction(type2, payload2Creator, meta2Creator),
}
How about this?
const reducer = handleActions({
SELECT_PRODUCT: [select, setColor],
UNSELECT_PRODUCT: [unselect, unsetColor]
}, initialState);
export const isLoading = createAction('IS_LOADING',(isLoadingFlag,message) => {
return {
isLoading:isLoadingFlag,
message:message,
}
})
/// creation action without payload creator
export const isLoadingWithDefault = createAction('IS_LOADING')
// Using the action creators
this.props.isLoading(new Error("Message!!!!!")) // => error doesn't exist is isn't populated and payload:object
this.props.isLoadingWithDefault(new Error("Message!!!")) // => error:true payload: is the error object
For async action, rejected promise then wrap to FSA: {error: true, payload: [Error Object]}
, always we should {return state}
.
FSA says if the status is error, the payload SHOULD be an error object
, i can't understand why handleAction/handleActions: If a single reducer is passed, it is used to handle both normal actions and failed actions
.
Hi.
Say I have one action initialize
, for this as for many actions I am dispatching an action:start
, action:success
and action:failure
. Most of these have counterparts in the reducers where they update properties like isFetching
, isSaving
, etc.
If I use redux-actions
instead of redux-thunk
then how do I let my reducers know the correct state at the correct time? If I can only dispatch one event at the end of one action then I can't dispatch multiple actions/events from one action ..
At the moment handleAction
falls back to just returning the current state instead of throwing an exception when the passed in reducer is not a function. This is very surprising, especially because the documentation does not mention it. IMHO it would make more sense to assume the passed in reducer (or the properties of the reducer map) are always functions and blow up if they are not.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.