遵循FSA(Flux Standard Action)规则
简单看了下,这个规则要点是:
action结构
- type
- payload(有效数据 或者 在error为true时候为一个错误对象)
- error(错误标识)
- meta(非有效数据的其他数据)
type是必须的,其他三个可选。规定action不能再有其他属性了。
错误处理
不要通过类似GET_DATA_SUCCESS和GET_DATA_FAIL这样的type去处理错误。而是通过设置error:true处理
{
type: 'GET_DATA',
payload: new Error(),
error: true
}
本质是actionCreater,并不是action
redux-actions是创建各种actionCreater,action只是简写。调用生成的actionCreater函数才会产生真正的action。
例如
const a = createAction('INCREMENT');
console.log(a(111))
产生的结果是
{
type: 'INCREMENT',
payload: 111
}
最简单的流程
1. 创建一个actionCreater
const actionCreater = createAction('INCREMENT');
2. 通过actionCreater创建reducer
const reducer = handleAction(
actionCreator,
(state, action) => ({
...state,
counter: state.counter + 1,
}),
{ counter: 1 }
);
3. 调用dispatch
dispatch(actionCreater())
通过以上三步,可以看出端倪了,但是并没什么用,我们用redux的痛点是什么?一堆switch,dispatch时候type乱飞等。下面让我们用redux-actions来实现经典官方例子todo。
实现一个简单的todo
actions
export const add = createAction('ADD_TODO');
export const del = createAction('DELETE_TODO');
export const toggle = createAction('TOGGLE_TODO');
reducers
const defaultState = [];
const reducer = handleActions({
[add]: (state, {payload}) => {
return [...state, {
text: payload,
done: false
}];
},
[del]: (state, {payload}) => {
const ind = state.findIndex(item => item.text === payload);
return [...state.slice(0, ind), ...state.slice(ind + 1)];
},
[toggle]: (state, {payload}) => {
const ind = state.findIndex(item => item.text === payload);
const todo = state[ind];
console.log(ind, todo)
return [
...state.slice(0, ind),
{
...todo,
done: !todo.done
},
...state.slice(ind + 1)
]
}
}, defaultState)
组件
function App() {
const [todo, setTodo] = useState('');
const dispatch = useDispatch();
const state = useSelector(state => state);
console.log('app');
function handleChange(e) {
setTodo(e.target.value);
}
function handleAdd() {
dispatch(add(todo));
setTodo('');
}
return (
<div className="App">
<input value={todo} onChange={handleChange} />
<button onClick={handleAdd}>Add</button>
<br />
<ul>
{state.map(({ text, done }) => (
<li key={text}>
<span
onClick={() => dispatch(toggle(text))}
style={done ? { textDecoration: 'line-through' } : null}
>
{text}
</span>
<button onClick={() => dispatch(del(text))}>X</button>
</li>
))}
</ul>
</div>
);
}
- reducer中不用写啰嗦的switch了
- 组件中也可以引入actionCreator生成action了
优势
1.reducer中无需写啰嗦的switch了
2.定义action(其实是actionCreator),多处引用,避免混乱
定义action
const increment = createAction('INCREMENT')
在reducer中引用
const reducer = handleActionsWithImmer({
[increment]: (state) => {
return state + 1;
},
...
在组件中引用
dispatch(increment());
3.自动处理异常
const noop = createAction('NOOP');
const error = new TypeError('not a number');
noop(error);
将自动生成一个error为true的action
{
type: 'NOOP',
payload: error,
error: true
}
在reducer中,可以自行判断error为true作处理,也可以如下声明式处理。这样,正常action会走next处理,如果error为true的action会走throw处理
handleAction(noop, {
next(state, action) {
return action.payload;
},
throw(state, action) {
console.log('出错了', state, action);
return state;
}
}, 'xxxxx');