Redux——结合案例一步步深入学习,百度、阿里、滴滴、新浪的面试心经总结

  • 在之前的案例里我们发现

我们发现我们使用redux状态的时候,

我们发现在组件里面都需要引入store

定义组件的初始化状态

监听redux状态的变化

  • 如何使用react-redux

需要在src/index.js上面使用Providestore属性

需要在components/todolist/TodoInfo.js文件使用redux状态

  • 使得actionCreators变得更加的纯粹,只需要return action就可以了。通过connect()高阶组件

const mapDispatchToProps = actionCreators

export default connect(null,mapDispatchToProps)(TodoInput)

connect(mapState,mapDispatch)(reactUI组件)

第一个mapState就是可以让组件通过属性props的方式去访问redux的共享状态

第二个mapDispatch可以是一个函数Object可以让组件通过属性的方式去访问到更改redux状态的方法了,并且派发action到reducer

实现效果


在这里插入图片描述

在这里插入图片描述

实现思路


1.封装组件

src/App.js

import React,{Component} from ‘react’;

import TodoList from “./components/todolist”

import Calculator from “./components/calculator”

class App extends Component{

render(){

return (

)

}

}

export default App;

src/index.js

需要在src/index.js上面使用Providestore属性

import React from ‘react’;

import ReactDOM from ‘react-dom’;

import App from ‘./App’;

import store from “./store”

import {Provider} from “react-redux”

ReactDOM.render(

//这边通过store属性传下去,那么所有的Provider的后代组件都可以通过connect连接,然后获取redux的状态了

,

document.getElementById(‘root’)

);

2.redux 和 react-redux

reducer状态的拆分

src/store/index

import {createStore} from “redux”

import reducer from “./reducer” //这个地方的reducer是合并好了的reducer

//可以通过createStore()创建一个store,参数需要接受reducer

const store = createStore(reducer)

export default store

src/store/reducer.js

通过combineReducers来去合并所有的分支reducer

import {combineReducers} from “redux”

import {combineReducers} from “redux”

import todolist from “./todolist/reducer”

import calculator from “./calculator/reducer”

//通过combineReducers来去合并所有的分支reducer

const reducer =combineReducers({

todolist,

calculator

})

export default reducer

TodoList

src/store/todolist/state.js

export default {

todos:[

{id:1,title:“今天周一”,isFinished:false},

{id:2,title:“检查作业”,isFinished:true}

]

}

src/store/todolist/reducer.js

//reducer必须是一个纯函数

//固定的输入必须要有固定的输出,内部函数不能有不纯粹的操作 Math.random() new Date()

//不能对之前的状态进行任何的更改 内部必须是同步的

//redux思想 状态与视图是一一对应的

//新的状态与之前的状态通过引用地址进行比较的,如果操作的新状态的地址与原状态一样,内部就会认为没有返回新的状态,那么视图也不会有新的变化

import state from “./state”

import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

const reducer = (prevState = state,action)=>{ //prevState={todos:[]}

let new_state={…prevState} //new_state={todos:[]} 浅拷贝 {…}object.assign vs 深拷贝 (递归, 对象 JSON stringify parse)

switch(action.type){

case ADD_NEW_TODO:

new_state.todos=new_state.todos.slice() //将之前的状态拷贝出来新的一份地址不一样

new_state.todos.push({id:handler.getId(new_state.todos),title:action.title,isFinished:false})

break;

case CHANGE_NEW_TODO:

new_state.todos=handler.changeNewTodo(new_state.todos,action.id)

break;

case REMOVE_NEW_TODO:

new_state.todos=handler.removeNewTodo(new_state.todos,action.id)

break;

default:

break;

}

return new_state //通过store.getState()其实就是获取到了reducer返回的内容

}

export default reducer

const handler={

getId(todos){

todos=todos.slice()

if(todos.length === 0) return 1;

return todos.sort((a,b)=>{

return b.id-a.id

})[0].id+1

},

changeNewTodo(todos,id){

todos=todos.slice()

for(var i=0;i<todos.length;i++){

if(todos[i].id===id){ //正是想要更改的这一项

todos[i].isFinished=!todos[i].isFinished

}

}

return todos

},

removeNewTodo(todos,id){

todos=todos.slice()

return todos.filter(item=> item.id!==id)

}

}

src/store/todolist/actionType.js

import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

export default{ //面试题? actionCreators or action 关系? actionCreators创建action action 一般是一个对象 type 通常写为type

addNewTodo(title){

let action={ //一般的action是具有标志性的信息的对象

type:ADD_NEW_TODO,

title

}

//需要将action给reducer传递过去

return action

},

changeNewTodo(id){

let action={

type:CHANGE_NEW_TODO,

id

}

return action

},

removeNewTodo(id){

let action={

type:REMOVE_NEW_TODO,

id

}

return action

}

}

src/store/todolist/actionCreators

import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

export default{ //面试题? actionCreators or action 关系? actionCreators创建action action 一般是一个对象 type 通常写为type

addNewTodo(title){

let action={ //一般的action是具有标志性的信息的对象

type:ADD_NEW_TODO,

title

}

//需要将action给reducer传递过去

return action

},

changeNewTodo(id){

let action={

type:CHANGE_NEW_TODO,

id

}

return action

},

removeNewTodo(id){

let action={

type:REMOVE_NEW_TODO,

id

}

return action

}

}

Calculator

src/store/caculator/state.js

export default{

prevNumber:0,

nextNumber:0,

operator:“+”,

result:0

}

src/store/caculator/reducer.js

import state from “./state”

import {CHANGE_NUMBER,COMPUTE} from “./actionType”

const reducer=(prevState=state,action)=>{

let new_state={…prevState}

switch(action.type){

case CHANGE_NUMBER:

new_state=handler.changeNumber(new_state,action.payload.numType,action.payload.num)

break;

case COMPUTE:

new_state=handler.compute(new_state)

break;

default:

break;

}

return new_state

}

export default reducer

const handler={

changeNumber(state,numType,num){ //new_state={pervNumber,nextNumber}

state[numType]=num

return state

},

compute(state){

state[“result”]=state.prevNumber1+state.nextNumber1

return state

}

}

src/store/caculator/actionType.js

export const CHANGE_NUMBER=“CHANGE_NUMBER”

export const COMPUTE=“COMPUTE”

src/store/caculator/actionCreator.js

import {CHANGE_NUMBER,COMPUTE} from “./actionType”

export default{

changeNumber(numType,num){ //这些方法成为actionCreators

let action={

type:CHANGE_NUMBER,

payload:{

numType,

num

}

}

return action

},

compute(){

let action={

type:COMPUTE

}

return action

}

}

src/group/caculator-group.js

import {connect} from “react-redux”

import actionCreators from “…/calculator/actionCreators”

export default connect(state=>state.calculator,actionCreators)

3.组件

TodoList组件

src/components/todolist/index.js

import React, { Component } from ‘react’

import TodoInput from “./TodoInput”

import TodoContent from “./TodoContent”

import TodoInfo from “./TodoInfo”

export default class index extends Component {

render() {

return (

)

}

}

src/components/todolist/TodoContent.js

import React, { Component } from ‘react’

import actionCreators from “…/…/store/todolist/actionCreators”

import { connect } from “react-redux”

//函数式组件(无状态组件)

const LiItem = props => {

const handleChange = () => {

//需要更改redux的状态,所以应该派发action进行更改

props.pp.changeNewTodo(props.item.id)

}

const handleRemove = () => {

props.pp.removeNewTodo(props.item.id)

}

return (

  • {props.item.title}

    删除

    )

    }

    class TodoContent extends Component {

    renderItem() {

    // console.log(this.props) //{todos,三个更改状态的方法} {todos: Array(2), addNewTodo: ƒ, changeNewTodo: ƒ, removeNewTodo: ƒ}

    let { todos } = this.props

    return (

    todos.map(item => {

    return (

    )

    })

    )

    }

    render() {

    return (

      {this.renderItem()}

      )

      }

      }

      //connect(mapStateToProps,mapDispatchToProps)(UIComponent)

      //mapStateToProps是一个函数,这个函数参数是state,可以让UIComponent通过this.props获取到redux的状态。

      //是一个函数,这个函数参数是dispatch,可以让UIComponent通过this.props获取到更改redux状态的actionCreators方法。

      const mapStateToProps = state => {

      return state.todolist

      }

      const mapDispatchToProps = actionCreators

      export default connect(mapStateToProps, mapDispatchToProps)(TodoContent)

      src/components/todolist/TodoInfo.js

      import React, { Component } from ‘react’

      import {connect} from “react-redux”

      class TodoInfo extends Component {

      computed() {

      let { todos } = this.props

      let all = { total: 0, finish: 0, unfinish: 0 }

      if (todos.length === 0) return all;

      all.total = todos.length

      todos.forEach(item => {

      if (item.isFinished) {

      all.finish++

      } else {

      all.unfinish++

      }

      })

      return all

      }

      //这个钩子函数只有当外部传入进来的属性发生变化的时候,此钩子函数才会执行

      UNSAFE_componentWillReceiveProps(){

      console.log(111111)

      }

      render() {

      let all = this.computed()

      return (

      总共{all.total}条

      完成了{all.finish}条

      未完成{all.unfinish}条

      )

      }

      }

      //connect()(UI组件/傻瓜组件木偶组件/展示组件)===>返回一个容器组件/智能组件

      //connect() 是一个HOC高阶组件,本质上就是一个函数,可以接收一个UI组件,返回一个新的组件 /shouldComponent PureComponent React.memo(组件)

      //这个函数返回什么,TodoInfo这个组件的属性上面就会有什么

      //我们发现参数上面的state其实就是store.getState()之后的结果

      //其实当redux的状态发生变化的时候,容器组件可以监听到状态的变化,然后把最新的状态通过属性的方式传递给了TodoInfo组件

      //因为容器组件内部已经帮助我们实现了store.subscribe方法的订阅了

      const mapStateToProps=state=>{

      return state.todolist

      }

      export default connect(mapStateToProps)(TodoInfo)

      src/components/todolist/TodoInput

      import React, { Component, createRef } from ‘react’

      import actionCreators from “…/…/store/todolist/actionCreators”

      import { connect } from ‘react-redux’

      class TodoInput extends Component {

      constructor() {

      super()

      this.input = createRef()

      }

      handleKeyUp = (e) => {

      if (e.keyCode === 13) {

      //通过actionCreators创建action派发给reducer进行处理

      this.props.addNewTodo(this.input.current.value)

      this.input.current.value = “”

      }

      }

      render() {

      return (

      )

      }

      }

      //这个方法返回什么,UI组件的属性上面就会有什么

      //方法作用就是可以在这里面挂载一些更改redux状态的方法,UI组件可以通过属性去访问到

      // const mapDispatchToProps=dispath=>{

      // return {

      // addNewTodo:title=>{

      // let action=actionCreators.addNewTodo(title)

      // dispath(action)

      // }

      // }

      // }

      //将所有的更改redux的状态的方法全都挂载到UI组件的属性上面去,并且内部会自动的将action派发给reducer

      const mapDispatchToProps = actionCreators

      export default connect(null,mapDispatchToProps)(TodoInput)

      Calculator组件

      src/components/calculator/index.js

      import React, { Component } from ‘react’

      import Expression from “./Expression”

      import Result from “./Result”

      export default class index extends Component {

      render() {

      return (

      =

      )

      }

      }

      src/components/calculator/Result.js

      import React, { Component } from ‘react’

      import CalculatorGroup from “…/…/store/group/calculator-group”

      class Result extends Component {

      render() {

      return (

      {this.props.result}

      )

      }

      }

      export default CalculatorGroup(Result)

      src/components/calculator/Expression.js

      import React, { Component, Fragment } from ‘react’

      import CalculatorGroup from “…/…/store/group/calculator-group”

      class Expression extends Component {

      handleChange=(e)=>{

      // console.log(e.target.id,e.target.value)

      this.props.changeNumber(e.target.id,e.target.value)

      }

      compute=()=>{

      this.props.compute()

      }

      render() {

      let {prevNumber,nextNumber,operator}=this.props

      return (

      {operator}

      )

      }

      }

      export default CalculatorGroup(Expression)

      //Expression组件上面就可以通过this.props获取到redux状态与更改状态的所有的方法了

      //connect(state=>state.calculator,actionCerators)(Expression)

      redux-thunk 中间件

      ==========================================================================

      使用方式: npm i redux-thunk

      可以在action里进行异步操作

      需要更改我们 store/index.js文件

      import thunk from “redux-thunk”

      const store = createStore(reducer,applyMiddleware(thunk))

      function createThunkMiddleware(extraArgument) {

      return ({ dispatch, getState }) => (next) => (action) => {

      if (typeof action === ‘function’) {

      return action(dispatch, getState, extraArgument);

      }

      return next(action);

      };

      }

      const thunk = createThunkMiddleware();

      thunk.withExtraArgument = createThunkMiddleware;

      export default thunk;

      • 核心代码

      判断是否是一个函数,如果是的话进行异步操作

      if (typeof action === ‘function’) {

      return action(dispatch, getState, extraArgument);

      }

      return next(action);

      实现效果


      在这里插入图片描述

      实现思想


      src/store/index.js

      import {createStore, applyMiddleware} from “redux”

      import reducer from “./reducer”

      import thunk from “redux-thunk”

      //第一个参数是合并的reducer

      //第二个参数是使用中间件

      const store = createStore(reducer,applyMiddleware(thunk))

      export default store

      src/store/calculator/actionCreators.js

      import {CHANGE_NUMBER,COMPUTE} from “./actionType”

      export default{

      changeNumber(numType,num){ //这些方法成为actionCreators

      let action={ //定义了具有标志信息的action对象,然后将这个action传递给了redux-thunk中间件

      type:CHANGE_NUMBER,

      payload:{

      numType,

      num

      }

      }

      return action

      },

      // compute(){ //这个是前端点击+号按钮的时候触发的方法

      // setTimeout(()=>{

      // let action={

      // type:COMPUTE

      // }

      // return action

      // },1000)

      // }

      /*

      我们在redux的actionCreators里面智能做一些同步操作,但是后续如果有异步需求,例如ajas操作,它就实现不了

      我们只能通过安装redux的一些中间件帮助实现我们的异步操作。redux-thunk redux-sage redux-promise

      redux的中间件? actionCreators创建的action到达reducer中间的过程

      之前的同步操作

      actionCreators ===> 自动的调用dispatch(action) ===> reducer ===> 返回新状态给store ===> react组件view

      安装中间件之后的异步操作

      actionCreators ===> middleware处理后 ===> 手动调用dispatch(action) ===> reducer ===> 返回新状态给store ===> react组件view

      redux-thunk 源码就会根据 actionCreators的return是一个对象还是一个函数?

      如果是对象的话,那么内部就会自动的派发action的reducer进行处理(同步)

      如果是函数的话,那么内部就可以提供一个参数dispatch给你,让你自己选择对应的时机进行dispatch(action)给reducer处理

      redux-thunk 其实就是增强了派发功能。 store.dispatch(action)的增强

      */

      //dispatch 可以更换

      compute(){

      return dispatch=>{ //这里的actionCreators返回的是一个函数,那么交给redux-thunk处理 action(dispatch)

      setTimeout(()=>{ //如果这里采用函数的写法,那么内部就可以写一些异步业务逻辑了。

      dispatch({type:COMPUTE})

      },1000)

      }

      }

      }

      redux使用总结(以TodoList案例为例)

      ===================================================================================

      安装npm i redux react-redux redux-thunk

      1.创建redux管理,reducer的派发,

      src/store/todolist/state.js

      export default {

      todos:[

      {id:1,title:“今天周一”,isFinished:false},

      {id:2,title:“检查作业”,isFinished:true}

      ]

      }

      src/store/todolist/reducer.js

      //reducer必须是一个纯函数

      //固定的输入必须要有固定的输出,内部函数不能有不纯粹的操作 Math.random() new Date()

      //不能对之前的状态进行任何的更改 内部必须是同步的

      //redux思想 状态与视图是一一对应的

      //新的状态与之前的状态通过引用地址进行比较的,如果操作的新状态的地址与原状态一样,内部就会认为没有返回新的状态,那么视图也不会有新的变化

      import state from “./state”

      import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

      const reducer = (prevState = state,action)=>{ //prevState={todos:[]}

      let new_state={…prevState} //new_state={todos:[]} 浅拷贝 {…}object.assign vs 深拷贝 (递归, 对象 JSON stringify parse)

      switch(action.type){

      case ADD_NEW_TODO:

      new_state.todos=new_state.todos.slice() //将之前的状态拷贝出来新的一份地址不一样

      new_state.todos.push({id:handler.getId(new_state.todos),title:action.title,isFinished:false})

      break;

      case CHANGE_NEW_TODO:

      new_state.todos=handler.changeNewTodo(new_state.todos,action.id)

      break;

      case REMOVE_NEW_TODO:

      new_state.todos=handler.removeNewTodo(new_state.todos,action.id)

      break;

      default:

      break;

      }

      return new_state //通过store.getState()其实就是获取到了reducer返回的内容

      }

      export default reducer

      const handler={

      getId(todos){

      todos=todos.slice()

      if(todos.length === 0) return 1;

      return todos.sort((a,b)=>{

      return b.id-a.id

      })[0].id+1

      },

      changeNewTodo(todos,id){

      todos=todos.slice()

      for(var i=0;i<todos.length;i++){

      if(todos[i].id===id){ //正是想要更改的这一项

      todos[i].isFinished=!todos[i].isFinished

      }

      }

      return todos

      },

      removeNewTodo(todos,id){

      todos=todos.slice()

      return todos.filter(item=> item.id!==id)

      }

      }

      src/store/todolist/actionType.js

      import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

      export default{ //面试题? actionCreators or action 关系? actionCreators创建action action 一般是一个对象 type 通常写为type

      addNewTodo(title){

      let action={ //一般的action是具有标志性的信息的对象

      type:ADD_NEW_TODO,

      title

      }

      //需要将action给reducer传递过去

      return action

      },

      changeNewTodo(id){

      let action={

      type:CHANGE_NEW_TODO,

      id

      }

      return action

      },

      removeNewTodo(id){

      let action={

      type:REMOVE_NEW_TODO,

      id

      }

      return action

      }

      }

      src/store/todolist/actionCreators

      import {ADD_NEW_TODO,REMOVE_NEW_TODO,CHANGE_NEW_TODO} from “./actionType”

      export default{ //面试题? actionCreators or action 关系? actionCreators创建action action 一般是一个对象 type 通常写为type

      addNewTodo(title){

      let action={ //一般的action是具有标志性的信息的对象

      type:ADD_NEW_TODO,

      title

      }

      //需要将action给reducer传递过去

      return action

      },

      changeNewTodo(id){

      let action={

      type:CHANGE_NEW_TODO,

      id

      }

      return action

      },

      removeNewTodo(id){

      let action={

      type:REMOVE_NEW_TODO,

      id

      }

      return action

      }

      }

      2.在组件中的使用,react-redux的使用

      • Provider包裹组件,添加store属性

      src/index.js

      引入import store from "./store"import {Provider} from "react-redux"

      Provider包裹组件,添加store属性

      import React from ‘react’;

      import ReactDOM from ‘react-dom’;

      import App from ‘./App’;

      import store from “./store”

      import {Provider} from “react-redux”

      ReactDOM.render(

      //这边通过store属性传下去,那么所有的Provider的后代组件都可以通过connect连接,然后获取redux的状态了

      ,

      document.getElementById(‘root’)

      );

      • 利用connect()高阶组件,将redux的共享状态和方法可以通过属性访问得到并使用

      src/components/todolist/TodoCotent.js

      import React, { Component } from ‘react’

      import actionCreators from “…/…/store/todolist/actionCreators”

      import { connect } from “react-redux”

      //函数式组件(无状态组件)

      自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

      深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

      因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
      img
      img
      img
      img
      img
      img

      既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

      由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

      如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
      img

      最后

      最后写上我自己一直喜欢的一句名言:世界上只有一种真正的英雄主义就是在认清生活真相之后仍然热爱它

      **

      src/index.js

      引入import store from "./store"import {Provider} from "react-redux"

      Provider包裹组件,添加store属性

      import React from ‘react’;

      import ReactDOM from ‘react-dom’;

      import App from ‘./App’;

      import store from “./store”

      import {Provider} from “react-redux”

      ReactDOM.render(

      //这边通过store属性传下去,那么所有的Provider的后代组件都可以通过connect连接,然后获取redux的状态了

      ,

      document.getElementById(‘root’)

      );

      • 利用connect()高阶组件,将redux的共享状态和方法可以通过属性访问得到并使用

      src/components/todolist/TodoCotent.js

      import React, { Component } from ‘react’

      import actionCreators from “…/…/store/todolist/actionCreators”

      import { connect } from “react-redux”

      //函数式组件(无状态组件)

      自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

      深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

      因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
      [外链图片转存中…(img-gSQEo025-1711987206520)]
      [外链图片转存中…(img-A0Gmjbon-1711987206521)]
      [外链图片转存中…(img-vaTWGLvG-1711987206521)]
      [外链图片转存中…(img-01AlhVFU-1711987206521)]
      [外链图片转存中…(img-P5fZ0JhJ-1711987206522)]
      [外链图片转存中…(img-NbVlnfOi-1711987206522)]

      既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

      由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

      如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
      [外链图片转存中…(img-GS7QAczf-1711987206522)]

      最后

      最后写上我自己一直喜欢的一句名言:世界上只有一种真正的英雄主义就是在认清生活真相之后仍然热爱它

    • 22
      点赞
    • 25
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值