redux react-redux简介
相对于vue的vuex而言 react的redux确实要复杂许多,并且没有相对完善适合初学者的文档,所以学习起来特别困难,这里用一个小例子来写一下 redux react-redux的简单用法,事先声明所有的文件地址请读者根据自己的文件地址写连接
首先是安装
// 个人比较喜欢用yarn 不喜欢的可以自行换成npm 或者 cnpm
yarn add redux --save
yarn add react-redux --save
- 在项目中创建store文件夹 并在其中创建actions.js 和 reducer.js
//redux定义修改state数据的唯一方法就是通过dispatch方法触发action
// 我们来定义一些待触发的action
let nextTodoId = 0
const addTodo = text => {
// console.log(text)
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
}
const setVisibilityFilter = filter => {
return {
type: 'SET_VISIBILITY_FILTER',
filter
}
}
const toggleTodo = id => {
return {
type: 'TOGGLE_TODO',
id
}
}
export {
addTodo,
setVisibilityFilter,
toggleTodo
}
接下来编写reducer
// reducer可以编写自己想存储的数据及方法 因为数据或者方法可能会有很多 所以可以使用
// combineReducers 方法将数据和方法合并在一起方便将来创建存储库
import { combineReducers } from 'redux'
// 生成要保存的todos数据 这里因为任务是集合 所以返回的是数组
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
case 'TOGGLE_TODO':
return state.map(todo =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
default:
return state
}
}
// 这里多写一个方法 提供给合并演示用 这里并未使用该方法 只为了方便读者理解combineReducers的作用
const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
const todoApp = combineReducers({
todos,
visibilityFilter
})
export default todoApp
写好reducer就可以创建自己的仓库了
- 在 store文件夹下创建一个index.js文件, 代码如下
import React from 'react';
// 从redux导入创建仓库的方法
import { createStore } from 'redux';
// 导入编写完成的reducer
import todo from './reducer'
const store = createStore(todo)
export default store
*warn这里引用文档中的一句话技术上讲,容器组件就是使用 store.subscribe() 从 Redux state 树中读取部分数据,并通过 props 来把这些数据提供给要渲染的组件。你可以手工来开发容器组件,但建议使用 React Redux 库的 connect() 方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染。(这样你就不必为了性能而手动实现 React 性能优化建议 中的 shouldComponentUpdate 方法。)
**使用 connect() 前,需要先定义 mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中。*例如,VisibleTodoList 需要计算传到 TodoList 中的 todos,所以定义了根据 state.visibilityFilter 来过滤 state.todos 的方法,并在 mapStateToProps 中使用。
其实就是说我们可以通过connect来写传入自己想要传递给组件的数据 而不友好是每次都用整个仓库这样并不友好
方法的就比如下面这种
import { connect } from 'react-redux'
import { toggleTodo } from '../../store/actions'
import Tasks from '../tasks/Tasks'
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}
const mapStateToProps = state => {
// console.log(state) // 所有的reducer方法
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
// connect的参数将作为数据传递给tasks组件
// 这里表示的是将todos和onToDoClick传入tasks组件的props中
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(Tasks)
export default VisibleTodoList
- 至此我们的redux仓库就创建完成了 接下来就是使用了
- 在项目的入口文件index.js中
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// provider可以魔法性的让所有的组件都访问到store
import { Provider } from 'react-redux'
// 导入定义好的store
import store from './store/index'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
- 准备工作完毕 接下来就是组件了
//首先是App.js
import React from 'react'
import Header from './components/header/Header'
import Tasks from './components/tasks/Tasks'
import Visiable from './components/visiable/visiable'
class App extends React.Component {
render() {
return (
<div className="App">
<Header />
<Visiable />
</div>
);
}
}
export default App;
//这个是header组件
import React from 'react'
import './Header.css'
import { connect } from 'react-redux'
import { addTodo } from '../../store/actions'
class Header extends React.Component {
constructor(props) {
super()
}
add = (e) => {
if(e.keyCode === 13) {
this.props.dispatch(addTodo(e.target.value))
}
}
render() {
return (
<div className="header_box">
<div><span className="title">ToDoList</span><input placeholder="添加todo" onKeyUp={this.add}/></div>
</div>
)
}
}
Header = connect()(Header)
export default Header
···
```javascript
//这里是任务组件
import React,{Component} from 'react';
import './Task.css';
import { PropTypes } from 'prop-types'; // 定义接收的值的类型
class Tasks extends Component {
render() {
return (
<div className="task_list">
<h3>{this.props.title}</h3>
<ul>
{this.props.todos.map(todo => {
return <li
className="item_task"
key={todo.id}
onClick={() => this.props.onTodoClick(todo.id)}
style={
{
textDecoration: todo.completed ? 'line-through' : 'none'
}
}
>
<div>
<input type="checkbox" />
<div>{todo.text}</div>
</div>
<span className="del">-</span>
</li>
})}
</ul>
</div>
);
}
}
Tasks.propTypes = {
todos: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired
).isRequired,
onTodoClick: PropTypes.func.isRequired
}
export default Tasks;