# 安装 Redux# 使状态完全从组件中分离开动作对应用状态的影响是用一个 reducer
来定义的 const counterReducer = ( state = 0 , action ) => { switch ( action. type) { case 'INCREMENT' : return state + 1 case 'DECREMENT' : return state - 1 case 'ZERO' : return 0 default : return state } }
第一个参数是当前状态,第二个参数是动作对象。
Reducer
不应该从应用代码中直接调用,只是作为创建存储的 createStore
函数的参数import { createStore } from 'redux' const counterReducer = ( state = 0 , action ) => { } const store = createStore ( counterReducer)
Redux
的 store
对象提供了 getState
方法,用于获取当前状态,以及 dispatch
方法,用于触发动作const store = createStore ( counterReducer) console. log ( store. getState ( ) ) store. dispatch ( { type : 'INCREMENT' } ) store. dispatch ( { type : 'INCREMENT' } ) store. dispatch ( { type : 'INCREMENT' } ) console. log ( store. getState ( ) ) store. dispatch ( { type : 'ZERO' } ) store. dispatch ( { type : 'DECREMENT' } ) console. log ( store. getState ( ) )
# 状态管理项目目录: redux-anecdotes/ ├── src/ │ ├── components/ │ │ └── AnecdoteList.jsx │ │ └── AnecdoteForm.jsx │ ├── store/ │ │ ├── actions/ │ │ │ └── anecdoteActions.js │ │ ├── reducers/ │ │ │ └── anecdoteReducer.js │ │ └── store.js │ ├── services/ │ ├── App.jsx │ └── main.jsx ├── public/ │ └── index.html ├── package.json └── README.md
在 app.jsx
中只有封装了 import AnecdoteForm from './components/AnecdoteForm' import AnecdoteList from './components/AnecdoteList' const App = ( ) => { return ( < div> < AnecdoteList /> < AnecdoteForm /> </ div> ) } export default App
所有的状态管理放在 Reducer
中,包括了点赞和添加的对象组成 const anecdotesAtStart = [ 'If it hurts, do it more often' , 'Adding manpower to a late software project makes it later!' , 'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.' , 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.' , 'Premature optimization is the root of all evil.' , 'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' ] const getId = ( ) => ( 100000 * Math. random ( ) ) . toFixed ( 0 ) const asObject = ( anecdote ) => { return { content : anecdote, id : getId ( ) , votes : 0 } } const initialState = anecdotesAtStart. map ( asObject) const reducer = ( state = initialState, action ) => { switch ( action. type) { case 'VOTE' : { const id = action. data. id const anecdoteToChange = state. find ( a => a. id === id) const changeanedote = { ... anecdoteToChange, votes : anecdoteToChange. votes + 1 } return state. map ( a => a. id !== id ? a : changeanedote) } case 'ADD' : { const newAnecdote = asObject ( action. data. content) return state. concat ( newAnecdote) } } return state } export const Vote = ( id ) => { return { type : 'VOTE' , data : { id } } } export const Add = ( content ) => { return { type : 'ADD' , data : { content } } } export default reducer
在 AnecdoteList.jsx
中,处理了显示数据和点赞的逻辑 通过调用 useDispatch
和 useSelector
来获取 store
中的状态和 dispatch
方法 import { useDispatch, useSelector } from "react-redux" import { Vote } from "../reducers/anecdoteReducer" const AnecdoteList = ( ) => { const anecdotes = useSelector ( state => state) const dispatch = useDispatch ( ) const vote = ( id ) => { dispatch ( Vote ( id) ) } anecdotes. sort ( ( a, b ) => a. votes - b. votes) return ( < div> < h2> Anecdotes </ h2> { anecdotes. map ( anecdote => < div key = { anecdote. id} > < div> { anecdote. content} </ div> < div> has { anecdote. votes} < button onClick = { ( ) => vote ( anecdote. id) } > vote </ button> </ div> </ div> ) } </ div> ) } export default AnecdoteList
在 AnecdoteForm.jsx
中,处理了添加新 anecdote 的逻辑 通过调用 useDispatch
来获取 store
中的 dispatch
方法 import { useDispatch } from 'react-redux' import { Add } from '../reducers/anecdoteReducer' const AnecdoteForm = ( ) => { const dispatch = useDispatch ( ) const addanect = ( e ) => { e. preventDefault ( ) const content = e. target. anecdote. value e. target. anecdote. value = '' dispatch ( Add ( content) ) } return ( < div> < h2> create new </ h2> < form onSubmit = { addanect} > < div> < input name = ' anecdote' /> </ div> < button type = ' submit' > create </ button> </ form> </ div> ) } export default AnecdoteForm
在 main.jsx
中,将 App
组件渲染到 #root
元素中,并订阅 store
的变化,当 store
发生变化时,重新渲染 App
组件,主要通过 Provider
组件来提供 store
给 App
组件 import ReactDOM from 'react-dom/client' import { createStore } from 'redux' import { Provider } from 'react-redux' import App from './App' import reducer from './reducers/anecdoteReducer' const store = createStore ( reducer) ReactDOM. createRoot ( document. getElementById ( 'root' ) ) . render ( < Provider store = { store} > < App /> </ Provider > )
# 再来点更多的 reducers对于我们的 state
可以有多个子状态,比如 anecdotes
和 filter
,所以我们需要创建多个 reducer
来处理 anecdotes
和 filter
的状态。 对于各自的 reducer
就写自己的逻辑,比如 anecdotes
的 reducer
可以这样写:
const anecdotesAtStart = [ 'If it hurts, do it more often' , 'Adding manpower to a late software project makes it later!' , 'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.' , 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.' , 'Premature optimization is the root of all evil.' , 'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' ] const getId = ( ) => ( 100000 * Math. random ( ) ) . toFixed ( 0 ) const asObject = ( anecdote ) => { return { content : anecdote, id : getId ( ) , votes : 0 } } const initialState = anecdotesAtStart. map ( asObject) const anecdoteReducer = ( state = initialState, action ) => { switch ( action. type) { case 'VOTE' : { const id = action. data. id const anecdoteToChange = state. find ( a => a. id === id) const changeanedote = { ... anecdoteToChange, votes : anecdoteToChange. votes + 1 } return state. map ( a => a. id !== id ? a : changeanedote) } case 'ADD' : { const newAnecdote = asObject ( action. data. content) return state. concat ( newAnecdote) } default : return state } } export const Vote = ( id ) => { return { type : 'VOTE' , data : { id } } } export const Add = ( content ) => { return { type : 'ADD' , data : { content } } } export default anecdoteReducer
而对于 notice
的 reducer
可以这样写:
const notificationAtStart = "you did something" const notificationReducer = ( state = notificationAtStart, action ) => { switch ( action. type) { case 'NOTIFICATION' : return action. data default : return state } } export const createNotification = ( content ) => { return { type : 'NOTIFICATION' , data : content } } export default notificationReducer
一个是通知一个是显示,两种逻辑 在 main.js
中我们需要引入两个 reducer
,并创建 store
,然后把 store
传递给 App
组件,这样 App
组件就可以通过 useSelector
和 useDispatch
来获取 store
中的状态和 dispatch
方法。 这里我们使用 Redux
工具包的 configureStore
函数创建商店
const store = configureStore ( { reducer : { anecdotes : anecdoteReducer, notification : notificationReducer } } )
state
就是一个包含两个子状态的对象, anecdotes
和 notification
,分别对应 anecdoteReducer
和 notificationReducer
在访问的时候就要注意了