react基础04-redux、react-redux、纯函数和高阶函数、serve

antd基本使用

  1、安装:npm i antd

  2、引入和使用

import React, { Component } from 'react'
import { Button, DatePicker } from 'antd'
import {
  WechatOutlined,
  WeiboOutlined,
  SearchOutlined
} from '@ant-design/icons'
const { RangePicker } = DatePicker

export default class App extends Component {
  render() {
    return (
      <div>
        App
        <button>点我</button>
        <Button type="primary">按钮1</Button>
        <Button>按钮2</Button>
        <Button type="link">按钮3</Button>
        <Button type="primary" icon={<SearchOutlined />}>
          Search
        </Button>
        <WechatOutlined />
        <WeiboOutlined />
        <DatePicker />
        <RangePicker />
      </div>
    )
  }
}

  3、引入样式表

import 'antd/dist/antd.css'

 

antd样式的按需引入

  1、安装插件

    npm i react-app-rewired customize-cra babel-plugin-import

  2、根目录下创建config-overrides.js

const { override, fixBabelImports } = require('customize-cra')

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css'
  })
)

  3、package.json修改启动命令

    "start": "set BROWSER=none&& react-script start",
    "build": "react-script build",
    "test": "react-script test",

    改为

    "start": "set BROWSER=none&& react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",

  4、此时不需要再引入 antd.css 了,直接引入组件即可,重启后,antd组件的js和css都会按需加载

import { Button, DatePicker } from 'antd'

 

redux

redux是什么

  1、redux是一个专门用于状态管理的库,不是react的插件库

  2、它可以用在react、vue、angular等项目中,但基本与react配合使用

  3、作用:集中式管理react应用中多个组件共享的状态

什么情况下需要使用redux

  1、某个组件的状态,需要让其他组件可以随时拿到

  2、一个组件需要改变另一个组件的状态(非父子组件通信)

  3、总体原则:能不用就不用,组件间传值比较繁琐时考虑使用

redux工作流程

  

redux的三个核心概念

  1、action

    同步action,返回一个对象,包含2个属性:type、data  例:{ type: 'add', data: 10 }  这个data可以是任意数据类型

    异步action,返回一个函数,函数中第一个参数为dispatch,函数体中执行异步任务

  2、reducer

    用于初始化状态,加工状态

    加工时,根据旧的state和action,产生新的state的纯函数

  3、store

    将state、action、reducer联系在一起的对象

    如何得到store对象?

      import { createStore } from 'redux'

      const store = createStore(xxxReducer)

    如何使用?

      store.getState() // 得到state

      store.dispatch() // 派发同步或异步action,也可以直接派发reducer

      store.subscribe(被监听的) // 注册监听,当产生了新的state时自动调用

 

redux的核心api

  createStore() // 创建包含指定reducer的store对象

  applyMiddleware() // 使用基于redux的中间件

  combineReducer() // 合并多个reducer函数

 

redux使用

简洁版

  1、src/redux/store.js

import { createStore } from 'redux'

const countReducer = (prevState = 10, action) => {
  const { type, data } = action
  switch (type) {
    case 'increment':
      return prevState + data
    case 'decrement':
      return prevState - data
    default:
      return prevState
  }
}

export default createStore(countReducer)

  2、components/Count/index.jsx

import React, { Component } from 'react'
import store from '../../redux/store'

export default class Count extends Component {
  componentDidMount() {
    // 监测redux中状态的变化,一旦变化,就调用render
    store.subscribe(() => this.setState({})) // 通过this.setState({})触发render函数,传{}表示什么也不改,纯粹为了刷新
  }
  render() {
    return (
      <div>
        {/* 展示redux中的状态 */}
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br />
        <button onClick={() => store.dispatch({ type: 'increment', data: +this.selectNumber.value })} {/*触发redux更新*/} >
          +
        </button>
        <br />
        <button onClick={() => store.dispatch({ type: 'decrement', data: +this.selectNumber.value })}>
          -
        </button>
      </div>
    )
  }
}

  3、可以将检测redux的逻辑放在入口文件中:src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'

ReactDOM.render(<App />, document.querySelector('#root'))

store.subscribe(() => ReactDOM.render(<App />, document.querySelector('#root')))

    只要redux中的状态发生变化,App组件会重新render。有diff算法的加持,效率没问题。好处是不用在每个组件中通过this.setState()去触发当前组件的render函数

完整版:(使用redux推荐的action机制)

  1、src/redux/store.js

import { createStore } from 'redux'

const INCREMENT = 'increment',
  DECREMENT = 'decrement'

// 通过action对象去触发。实质是将{ type: 'increment', data: +this.selectNumber.value }在store中抽成函数(createIncrementAction),在触发reducer时调用createIncrementAction函数即可
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })

const countReducer = (prevState = 10, action) => {
  const { type, data } = action
  switch (type) {
    case INCREMENT:
      return prevState + data
    case DECREMENT:
      return prevState - data
    default:
      return prevState
  }
}

export default createStore(countReducer)

  2、components/Count/index.jsx

import React, { Component } from 'react'
import store, { createIncrementAction, createDecrementAction } from '../../redux/store'

export default class Count extends Component {
  render() {
    return (
      <div>
        {/* 展示redux中的状态 */}
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br />
        <button
          onClick={() =>
            // store.dispatch({type: 'increment',data: +this.selectNumber.value})
            store.dispatch(createIncrementAction(+this.selectNumber.value))
          } // 触发redux更新
        >
          +
        </button>
        <br />
        <button
          onClick={() =>
            // store.dispatch({type: 'decrement',data: +this.selectNumber.value})
            store.dispatch(createDecrementAction(+this.selectNumber.value))
          }
        >
          -
        </button>
      </div>
    )
  }
}

异步action的使用

  1、安装

    npm i redux-thunk

  2、src/redux/store.js

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action

const INCREMENT = 'increment',
  DECREMENT = 'decrement'

// 通过action对象去触发。实质是将{ type: 'increment', data: +this.selectNumber.value }在store中抽成函数(createIncrementAction),在触发reducer时调用createIncrementAction函数即可
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action 函数中一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
  setTimeout(() => dispatch(createIncrementAction(data)), time)

const countReducer = (prevState = 10, action) => {
  const { type, data } = action
  switch (type) {
    case INCREMENT:
      return prevState + data
    case DECREMENT:
      return prevState - data
    default:
      return prevState
  }
}

export default createStore(countReducer, applyMiddleware(thunk))

  3、components/Count/index.jsx

import React, { Component } from 'react'
import store, { createIncrementAsyncAction } from '../../redux/store'

export default class Count extends Component {
  render() {
    return (
      <div>
        {/* 展示redux中的状态 */}
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br />
        <button onClick={() => store.dispatch(createIncrementAsyncAction(+this.selectNumber.value, 500))}>
          异步加
        </button>
      </div>
    )
  }
}

 

react-redux

  react的插件库,简化redux的使用

  

  react-redux将所有组件分为两大类

    UI组件:

      只负责ui的呈现,不带有任何业务逻辑

      通过props接收数据(一般数据和函数)

      不使用任何redux的api

      一般保存在components文件夹下

    容器组件:

      负责管理数据和业务逻辑,不负责ui呈现

      使用redux的api

      一般保存在containers文件夹下

  使用:

    1、安装:npm i react-redux

    2、src/redux/store.js

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action

const INCREMENT = 'increment',
  DECREMENT = 'decrement'

export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
  setTimeout(() => dispatch(createIncrementAction(data)), time)

const countReducer = (prevState = 10, action) => {
  const { type, data } = action
  switch (type) {
    case INCREMENT:
      return prevState + data
    case DECREMENT:
      return prevState - data
    default:
      return prevState
  }
}

export default createStore(countReducer, applyMiddleware(thunk))

    3、入口文件:src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import { Provider } from 'react-redux'

// 通过Provider将store根据需要精准地将store传递给每个组件,这样就不需要在App组件中给每个组件都传递store={store}
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector('#root')
)

    4、src/containers/Count/index.jsx【react-redux核心:connect】

import React, { Component } from 'react'
import { connect } from 'react-redux'
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction
} from '../../redux/store'

class Count extends Component {
  render() {
    const { count, increment, decrement, incrementAsync } = this.props
    console.log(this.props)
    return (
      <div>
        <h1>当前求和为:{count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br />
        <button onClick={() => increment(+this.selectNumber.value)}>+</button>
        <br />
        <button onClick={() => decrement(+this.selectNumber.value)}>-</button>
        <br />
        <button onClick={() => incrementAsync(+this.selectNumber.value, 500)}>
          异步加
        </button>
      </div>
    )
  }
}

/*
  connect
    1、容器组件通过connect传递给UI组件两个值:
      ①redux中保存的状态
      ②用于操作redux状态的方法
    2、由react-redux处理,函数第一个函数可以收到state;第二个函数可以收到dispatch。这里不需要再引入store通过store.getState()获取state,也不需要通过store.dispath()去派发,直接用react-redux处理过的state和dispatch就行
    3、包裹UI组件,返回容器组件

*/
export default connect(
  (state) => ({ count: state }), // 第一个参数:mapStateToProps --> 映射redux中的state变成CountUI组件的props
  // 第二个参数:mapDispatchToProps --> 映射redux中的dispatch变成CountUI组件的props,并且不需要subscribe监听触发render函数进行更新,connect会自动触发视图更新
  /*
    // 一般写法(函数)
    (dispatch) => ({
      increment: (number) => dispatch(createIncrementAction(number)),
      decrement: (number) => dispatch(createDecrementAction(number)),
      incrementAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time))
    })
  */
  // 简写(对象)
  {
    increment: createIncrementAction, // react-redux会自动触发dispatch
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction
  }
)(Count)

    5、App.jsx

import React, { Component } from 'react'
import Count from './containers/Count'

export default class App extends Component {
  render() {
    return (
      <>
        <Count />
      </>
    )
  }
}

 

多个组件共享redux

  1、src/redux/store.js:通过combineReducers汇总reducer(类似vuex中的modules)

import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action

const INCREMENT = 'increment',
  DECREMENT = 'decrement',
  ADD_PERSON = 'add_person'

export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
  setTimeout(() => dispatch(createIncrementAction(data)), time)

export const createAddPersonAction = (data) => ({ type: ADD_PERSON, data })

const countReducer = (prevState = 10, action) => {
  const { type, data } = action
  switch (type) {
    case INCREMENT:
      return prevState + data
    case DECREMENT:
      return prevState - data
    default:
      return prevState
  }
}

const initPerson = [{ id: '001', name: '小明', age: 18 }]
const personReducer = (prevState = initPerson, action) => { // 必须是一个纯函数
  const { type, data } = action
  switch (type) {
    case ADD_PERSON:
      return [...prevState, data] // prevState.push(data); return prevState  将prevState传入,redux会更新数据,但不会更新视图。因为纯函数中不允许编辑prevState和action,而prevState.push()会更改prevState
    default:
      return prevState
  }
}

const reducers = combineReducers({
  count: countReducer,
  personList: personReducer
})

export default createStore(reducers, applyMiddleware(thunk))

  2、src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import { Provider } from 'react-redux'

ReactDOM.render( // Provider包裹App组件,让App组件的后代容器组件都能接收到store
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector('#root')
)

  3、src/containers/Count/index.jsx

import React, { Component } from 'react'
import { connect } from 'react-redux'
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction
} from '../../redux/store'

class Count extends Component {
  render() {
    const { count, increment, decrement, incrementAsync, personLength } =
      this.props
    console.log(this.props)
    return (
      <div>
        <h1>当前求和为:{count}</h1>
        <h1>Person组件长度:{personLength}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br />
        <button onClick={() => increment(+this.selectNumber.value)}>+</button>
        <br />
        <button onClick={() => decrement(+this.selectNumber.value)}>-</button>
        <br />
        <button onClick={() => incrementAsync(+this.selectNumber.value, 500)}>
          异步加
        </button>
      </div>
    )
  }
}

export default connect(
  (state) => ({ count: state.count, personLength: state.personList.length }),
  {
    increment: createIncrementAction,
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction
  }
)(Count)

  4、src/containers/Person/index.jsx

import React, { Component } from 'react'
import { nanoid } from 'nanoid' // 需安装nanoid
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/store'

class Person extends Component {
  render() {
    console.log(this.props)
    const { count, personList, createAddPersonAction } = this.props
    return (
      <div>
        <h2>Person组件</h2>
        <b>Count组件的值:{count}</b>
        <br />
        <input ref={(c) => (this.name = c)} type="text" placeholder="名字" />
        <input ref={(c) => (this.age = c)} type="text" placeholder="年龄" />
        <button
          onClick={() => {
            const name = this.name.value,
              age = this.age.value,
              obj = { id: nanoid(), name, age }
            createAddPersonAction(obj)
            this.name.value = ''
            this.age.value = ''
          }}
        >
          添加
        </button>
        <ul>
          {personList.map(({ id, name, age }) => (
            <li key={id}>
              {name}---{age}
            </li>
          ))}
        </ul>
      </div>
    )
  }
}
export default connect(({ personList, count }) => ({ personList, count }), {
  createAddPersonAction
})(Person)

  5、App.js

import React, { Component } from 'react'
import Count from './containers/Count'
import Person from './containers/Person'

export default class App extends Component {
  render() {
    return (
      <>
        <Count />
        <hr />
        <Person />
      </>
    )
  }
}

  6、效果

    

 

纯函数和高阶函数

  纯函数

    1、一种特别的函数,只要是同样的输入(实参),必定得到同样的输出(返回)

    2、必须遵守以下一些约束:

      ①不得改写参数数据

      ②不会产生任何副作用,例如:网络请求、输入和输出设备

      ③不能调用Date.now()或Math.rendom()等不纯的方法

    3、redux的reducer函数必须是一个纯函数,根据不同的action对象,分别对状态进行不同的处理,这里不能出现任何的DOM操作、ajax请求、计时器等不纯的方法

      

 

 

   高阶函数

    1、一种特别的函数,参数是函数或者返回值是函数

    2、常见的高阶函数:

      ①定时器中的函数

      ②数组的forEach、map、filter、reduce、find等方法,bind ……

      ③promise

      ④react-redux中的connect函数

    3、做用:能实现更加动态,更加可扩展的功能

 

redux调试工具的使用

  1、谷歌浏览器中上传redux开发工具

  2、安装插件:npm i redux-devtools-extension

  3、store.js

import { composeWithDevTools } from 'redux-devtools-extension'


export default createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunk)) // 如果不需要支持异步action就直接传入composeWithDevTools()
)

  4、效果

    

 

serve

  1、npm i serve -g

  2、打包当前项目:npm run build

  3、执行serve build(将build作为服务器的根路径启动一台服务器)

 

posted @ 2021-01-12 16:30  吴小明-  阅读(138)  评论(0编辑  收藏  举报