在新的应用中,绝对应该使用 hook-api,但是在使用 redux 维护老项目时,了解如何使用 connect 非常有用。

Using the connect-function to share the redux store to components

使用 connect-function 将 redux 存储共享给组件
将Notes组件修改为使用**connect**替换**useSelector****useDispatch**

mapStateToProps

connect接受的第一个参数是mapStateToProps,用于将state映射到组件的props,可代替useSelector
原组件用useSelector的写法

  1. const Notes = () => {
  2. const notes = useSelector(({filter, notes}) => {
  3. if ( filter === 'ALL' ) {
  4. return notes
  5. }
  6. return filter === 'IMPORTANT'
  7. ? notes.filter(note => note.important)
  8. : notes.filter(note => !note.important)
  9. })
  10. return(
  11. <ul>
  12. {notes.map(note =>
  13. // ...
  14. )}
  15. </ul>
  16. )
  17. }
  18. export default Notes

改用mapStateToProps的写法, 先在mapStateToProps中将state映射到props,然后在props中获取数据

  1. import { connect } from 'react-redux'
  2. const Notes = (props) => {
  3. return(
  4. <ul>
  5. {props.notes.map(note =>
  6. // ...
  7. )}
  8. </ul>
  9. )
  10. }
  11. // 注意返回值是一个对象
  12. const mapStateToProps = (state) => {
  13. if ( state.filter === 'ALL' ) {
  14. return {notes: state.notes}
  15. }
  16. return {notes: state.filter === 'IMPORTANT'
  17. ? state.notes.filter(note => note.important)
  18. : state.notes.filter(note => !note.important)}
  19. }
  20. export default connect(mapStateToProps)(Notes)

mapDispatchToProps

connect接受第二个参数mapDispatchToProps,用于将dispatch和action creator映射到props
原组件中dispatch的写法

  1. const Notes = () => {
  2. const dispatch = useDispatch()
  3. // ...
  4. return(
  5. <ul>
  6. {notes.map(note =>
  7. <Note
  8. //...
  9. handleClick={() =>
  10. dispatch(toggleImportanceOf(note.id))
  11. }
  12. />
  13. )}
  14. </ul>
  15. )
  16. }
  17. export default Notes

改用mapDispatchToProps,它将action creator转换为自带dispatch的形式,在props中直接调用action creator

  1. import { connect } from 'react-redux'
  2. const Notes = (props) => {
  3. return(
  4. <ul>
  5. {notes.map(note =>
  6. <Note
  7. //...
  8. handleClick={() =>
  9. props.toggleImportanceOf(note.id)
  10. }
  11. />
  12. )}
  13. </ul>
  14. )
  15. }
  16. // ...
  17. const mapDispatchToProps = {
  18. toggleImportanceOf
  19. }
  20. export default connect(mapStateToProps, mapDispatchToProps)(Notes)

如果有的组件中不需要mapStateToProps,则传null

  1. export default connect(null, { createNote })(NewNote)

Referencing action creators passed as props

在控制台中将普通的action creator和经过connect转化后在props中的action creator打印出来,可以看到两者的区别

  1. import React from 'react'
  2. import { connect } from 'react-redux'
  3. import { createNote } from '../reducers/noteReducer'
  4. const NewNote = (props) => {
  5. console.log(createNote)
  6. console.log(props.createNote)
  7. // ...
  8. }
  9. export default connect(null, { createNote })(NewNote)

image.png

Alternative way of using mapDispatchToProps

在上文中,mapDispatchToProps只是一个简单的javascript对象,大多数情况下使用这种形式就够了,但遇到更复杂的情况,比如需要用到组件的props,则需要将mapDispatchToProps写成函数形式

  1. const mapDispatchToProps = dispatch => {
  2. return {
  3. createNote: value => {
  4. dispatch(createNote(value))
  5. },
  6. }
  7. }

可以参考一个非常好的教程:Getting Started with Redux

Presentational/Container revisited

presentational component 表示层组件的特点:

  • Are concerned with how things look.
  • May contain both presentational and container components inside, and usually have some DOM markup and styles of their own.
  • Often allow containment via props.children.
  • Have no dependencies on the rest of the app, such as Redux actions or stores.
  • Don’t specify how the data is loaded or mutated.
  • Receive data and callbacks exclusively via props.
  • Rarely have their own state (when they do, it’s UI state rather than data).
  • Are written as functional components unless they need state, lifecycle hooks, or performance optimizations.

container component 容器组件的特点:

  • Are concerned with how things work.
  • May contain both presentational and container components inside but usually don’t have any DOM markup of their own except for some wrapping divs, and never have any styles.
  • Provide the data and behavior to presentational or other container components.
  • Call Redux actions and provide these as callbacks to the presentational components.
  • Are often stateful, as they tend to serve as data sources.
  • Are usually generated using higher order components such as connect from React Redux, rather than written by hand.

a high order component(HOC) is a function that accept a “regular” component as its parameter, that then returns a new “regular” component as its return value.
使用connect的组件是高阶组件

high order function(HOF)
所有处理数组的函数都是高阶函数,比如map, filter and find

React hook-api推出后,HOC越来越不受欢迎,因为hook-api要简单的多

Redux and the component state

使用React的正确方式:
React only focuses on generating the views, and the application state is separated completely from the React components and passed on to Redux, its actions, and its reducers.
React只专注于产生视图,应用的状态完全从组件中分离出来,由Redux管理
useState只在局部使用

我们应该一直使用Redux吗?
不,现在使用React context-api and the useReducer-hook也能实现类似Redux的状态管理

exercise 6.19-6.21

修复最后一条notification时间不足10秒的bug

  1. export const setNotification = (notification, seconds) => {
  2. return (dispatch) => {
  3. dispatch({
  4. type: 'SET_NOTIFICATION',
  5. notification,
  6. })
  7. if (window.timeoutID) {
  8. console.log(window.timeoutID)
  9. }
  10. window.timeoutID = setTimeout(() => {
  11. dispatch({
  12. type: 'SET_NOTIFICATION',
  13. notification: '',
  14. })
  15. }, seconds * 1000)
  16. }
  17. }

Q: timeoutID除了放window,放哪里更好?