immutable在redux中的应用主要集中在reducers中对state数据的操作上。
可以参考 stackoverflow中的问题
下面和结合一个实例关于immutable部分的介绍和注意的问题。
不使用immutable时的写法
数据结构(普通的对象数据):
const dummyTodos = [
{ id: 0, isDone: true, text: 'make components' },
{ id: 1, isDone: false, text: 'design actions' },
{ id: 2, isDone: false, text: 'implement reducer' },
{ id: 3, isDone: false, text: 'connect components' }
];
纯组件:
const Todo = ({ todo }) => {
if (todo.isDone) {
return <strike>{todo.text}</strike>
}
return <span>{todo.text}</span>
};
const TodoList = ({ todos }) => {
return (
<div>
<input
type="text"
placeholder="add a task"
/>
<ul className="todo__list">
{todos.map(todo => (
<li
key={todo.id}
className="todo__item"
>
<Todo todo={todo} />
</li>
)}
</ul>
</div>
);
}
这个todo app有2个主要的动作,'ADD_TODO', 'TOGGLE_TODO',下面来完成action creators,reducers等
action creators, reducers
action creators
// 产生一个随机的32位字符串
const uid = () => Math.randow().toString(34).slice(2);
const addTodo = (text) => ({
type: 'ADD_TODO',
payload: {
id: uid(),
text,
isDone: false
}
});
const toggleTodo = (id) => ({
type: 'TOGGLE_TODO',
payload: id
});
reducers 和 immutable js
在这里就涉及到数据的处理工作了,通过immutable来完成添加和更新数据。(这里actions比较少,直接将所有的actions.type都用一个reducer处理)
// 使用常用的Map, List集合
import { Map, List } from 'immutable';
# 初始化, 将初始状态变为List集合
const initState = List([]);
// 将数据解构变为immutable形式
List [
Map { id: 0, isDone: true, text: 'make components' },
Map {...},
Map {...}
...
]
const rootReducer = (state = initState, action) => {
switch (action.type) {
case 'ADD_TODO':
// 变为immutable之后,可以直接使用 'push' 来添加到数组
// 不用担心改变原来的数据
# 注意先将对象变为Map集合之后再push到 List集合中
return state.push(Map(action.payload));
case 'TOGGLE_TODO':
// 对于更新Map集合中的某个属性,使用immutable中update方法
// Map集合 update方法的其中一种形式 update(key, updater)
# 注意这里使用immutable中的'map'和'get'方法: todo.get('id')
return state.map(todo => {
if (todo.get('id') === action.payload) {
return todo.update('isDone', isDone => !isDone);
} else {
return todo;
}
});
default:
return state;
}
}
然后将逻辑写入纯组件中
const TodoList = ({ todos, toggleTodo, addTodo }) => {
const onSubmit = (e) => {
const input = e.target;
const text = input.value;
# 判断是否为 enter 键
const isEnterKey = e.which === 13;
const isLongEnough = text.length > 0;
if (isEnterKey && isLongEnough) {
addTodo(text);
input.value = '';
}
};
const toggleClick = (id) => () => toggleTodo(id);
return (
<div>
<input
type="text"
placeholder="add a task"
onKeyDown={onSubmit}
/>
<ul>
{todos.map(todo => (
<li
# 注意这里需要使用get来获取Map集合中的id
# 而不能直接的使用todo.id
key={todo.get('id')}
onClick={toggleClick(todo.get('id'))}
>
# 注意这里要将Map集合转变为普通JS对象
<Todo todo={todo} />
</li>
))}
</ul>
</div>
)
}
使用react-redux将状态和组件连接起来
import { connect } from 'react-redux';
const mapStateToProps = (state) => ({
todos: state
});
const mapDispatchToProps = (dispatch) => ({
addTodo: text => dispatch(addTodo(text)),
toggleTodo: id => dispatch(toggleTodo(id))
});
// 这相当于
<TodoList
todos={state}
addTodo={text => dispatch(addTodo(text))}
toggleTodo={id => dispatch(toggleTodo(id))}
/>
// 使用一个容器
const TodoListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
将store通过Provider传递下去
import { createStore } from 'redux';
import { Provider } from 'react-redux';
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<TodoListContainer />
</Provider>,
document.getElementById('root')
);
完成!