#创作灵感#
在学习和使用react-redux开发过程中,碰到过不同的react-redux的使用方式,在经过查阅资料和文档后总结了三种react-redux的使用方式,下面分别通过这三种方式来实现redux官方的todos示例
#示例截图#
一、使用connect来连接redux store和react组件
完整代码:redux-connect: 使用connect来连接redux store和react组件的方式实现redux官方示例中的todos示例
connect()相关文档:API · Redux
项目目录结构
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
// redux
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './store/index'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools())
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
App.js
import AddTodo from './containers/AddTodo'
import VisibleTodoList from './containers/VisibleTodoList'
import Footer from './components/Footer';
function App() {
return (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
);
}
export default App;
store/action.js
let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
export const toggleTodo = id => ({
type: 'TOGGLE_TODO',
id
})
export const setVisibilityFilter = filter => ({
type: 'SET_VISIBILITY_FILTER',
filter
})
export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
}
store/index.js
import { combineReducers } from "redux";
import todosReducer from './todosReducer'
import visibilityFilterReducer from './visibilityFilterReducer'
export default combineReducers({
todosReducer,
visibilityFilterReducer
})
store/todosReducer.js
const todosReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
{
text: action.text,
id: action.id,
completed: false
}
]
case 'TOGGLE_TODO':
return state.map(todo =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
default:
return state
}
}
export default todosReducer
store/ visibilityFilterReducer.js
import { VisibilityFilters } from './action'
const visibilityFilterReducer = (state = VisibilityFilters.SHOW_ALL, action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter
default:
return state
}
}
export default visibilityFilterReducer
containers/AddTodo.js
import { connect } from "react-redux";
import { addTodo } from '../store/action'
const AddTodo = ({dispatch}) => {
let input
const handleAddTodo = (e) => {
e.preventDefault()
if (!input.value.trim()) return
dispatch(addTodo(input.value))
input.value = ''
}
return (
<div>
<form onSubmit={(e)=>handleAddTodo(e)}>
<input ref={node => input = node} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
export default connect()(AddTodo)
containers/VisibleTodoList.js
- connect(mapStatetoProps,mapDispatchToProps)(TodoList)会将todos和toggleTodo注入到TodoList的props中
import { connect } from "react-redux";
import TodoList from "../components/TodoList";
import { toggleTodo, VisibilityFilters } from "../store/action";
const getFiltertodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(item => item.completed === false)
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(item => item.completed === true)
default:
return todos
}
}
const mapStateToProps = (state) => {
return {
todos: getFiltertodos(state.todosReducer, state.visibilityFilterReducer)
}
}
const mapDispatchToProps = (dispatch) => ({
toggleTodo: id => dispatch(toggleTodo(id))
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
components/TodoList.js
- 在ToodList组件中可以从props中把注入的todos, toggleTodo解构出来
import Todo from './Todo'
const TodoList = ({ todos, toggleTodo }) => {
return (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={()=>toggleTodo(todo.id)}
/>
)}
</ul>
)
}
export default TodoList
components/Todo.js
const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={{
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
)
export default Todo
components/Footer.js
import React from 'react'
import FilterLink from '../containers/FilterLink'
import { VisibilityFilters } from '../store/action'
const Footer = () => (
<div>
<span>Show: </span>
<FilterLink filter={VisibilityFilters.SHOW_ALL}>
All
</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>
Active
</FilterLink>
<FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>
Completed
</FilterLink>
</div>
)
export default Footer
containers/FilterLink.js
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../store/action'
import Link from '../components/Link'
// 第二个参数ownProps为Link组件的props
const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilterReducer
})
const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Link)
components/Link.js
import React from 'react'
const Link = ({onClick, active, children}) => (
<button
onClick={onClick}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
)
export default Link
二、在react Hook下使用redux使用,useSelector,useDispatch替换connect
在这种方式下不在需要使用connect去连接redux store和react组件并注入state和dispatch,可以直接在组件中使用useSelector来获取state、useDispatch获取dispatch
完整代码:redux-hook: 使用useSelector,useDispatch实现redux官方示例中todos示例
项目目录结构
在第一种方式的基础上修改
- 把AddTodo组件的封装提出来并删掉container文件夹
- 修改App.js和components下的组件
修改如下:
App.js
import AddTodo from './components/AddTodo'
// import VisibleTodoList from './containers/VisibleTodoList'
import TodoList from './components/TodoList';
import Footer from './components/Footer';
function App() {
return (
<div>
<AddTodo />
<TodoList />
<Footer />
</div>
);
}
export default App;
AddTodo.js
import { addTodo } from '../store/action'
import { useDispatch } from 'react-redux'
const AddTodo = () => {
let input
const dispatch = useDispatch()
const handleAddTodo = (e) => {
e.preventDefault()
if (!input.value.trim()) return
dispatch(addTodo(input.value))
input.value = ''
}
return (
<div>
<form onSubmit={(e)=>handleAddTodo(e)}>
<input ref={node => input = node} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
export default AddTodo
TodoList.js
import Todo from './Todo'
import { useSelector, useDispatch } from 'react-redux'
import { toggleTodo } from '../store/action'
import { VisibilityFilters } from '../store/action'
const TodoList = () => {
const dispatch = useDispatch()
const todos = useSelector(state => state.todosReducer)
const filter = useSelector(state => state.visibilityFilterReducer)
const getFiltertodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(item => item.completed === false)
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(item => item.completed === true)
default:
return todos
}
}
const visibilityTodos = getFiltertodos(todos, filter)
return (
<ul>
{visibilityTodos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={()=>dispatch(toggleTodo(todo.id))}
/>
)}
</ul>
)
}
export default TodoList
Footer.js
import React from 'react'
// import FilterLink from '../containers/FilterLink'
import Link from './Link'
import { VisibilityFilters } from '../store/action'
const Footer = () => (
<div>
<span>Show: </span>
<Link filter={VisibilityFilters.SHOW_ALL}>
All
</Link>
<Link filter={VisibilityFilters.SHOW_ACTIVE}>
Active
</Link>
<Link filter={VisibilityFilters.SHOW_COMPLETED}>
Completed
</Link>
</div>
)
export default Footer
Link.js
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { setVisibilityFilter } from '../store/action'
const Link = ({ children, filter}) => {
const dispatch = useDispatch()
const activeFilter = useSelector(state => state.visibilityFilterReducer)
const active = activeFilter === filter
return(
<button
onClick={()=>dispatch(setVisibilityFilter(filter))}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
)
}
export default Link
三、使用Redux Toolkit
这是官方推荐的方法,编写起来更加容易
完整代码:redux-toolkit: react-redux结合Redux Toolkit实现redux官方示例中todos示例
- 在第二种方法的基础下先下载Redux Toolkit npm i @reduxjs/toolkit
- 修改index.js和store文件夹啊下的js文件
修改如下:
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import store from './store/index'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
store/index.js
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from './todosReducer'
import visibilityFilterReducer from './visibilityFilterReducer'
const store = configureStore({
reducer: {
todos: todosReducer,
filter: visibilityFilterReducer
}
})
export default store
store/todosReducer.js
import { createSlice } from "@reduxjs/toolkit"
let nextTodoId = 0
const todosReducer = createSlice({
name: 'todos',
// 初始状态
initialState: {
todos:[]
},
// 修改数据的方法
reducers: {
addTodo(state,action){
state.todos.push({
text: action.payload,
id: nextTodoId++,
completed: false
})
},
toggleTodo(state, action) {
const item = state.todos.find(item => item.id === action.payload)
item.completed = !item.completed
}
}
})
export const {addTodo, toggleTodo} = todosReducer.actions
export default todosReducer.reducer
store/visibilityFilterReducer.js
import { VisibilityFilters } from './filters'
import { createSlice } from "@reduxjs/toolkit"
const filterReducer = createSlice({
name: 'filter',
// 初始状态
initialState: {
filter: VisibilityFilters.SHOW_ALL
},
// 修改数据的方法
reducers: {
setFilter(state,action) {
state.filter = action.payload
}
}
})
export const { setFilter } = filterReducer.actions
export default filterReducer.reducer