【REACT-redux和数据持久化react-redux】

1. redux介绍

1.1 描述

Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(state对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers),这样就可以进行数据追踪。

1.2 使用的三大原则

  • state以单一对象存储在store对象中
  • state只读(每次都会返回一个新的对象)
  • 使用纯函数reducer执行state更新

什么是纯函数?

1.3 官方文档

英文文档
中文文档
Github

2. 安装redux相关包

npm i redux
// 以下两个是redux中间件,用来处理异步的action creator
npm i redux-thunk
npm i redux-promise

3. 创建目录关系

src下创建文件夹redux
在这里插入图片描述

3.1 store.js

import {createStore,combineReducers, applyMiddleware,compose} from 'redux';
import TabbarReducer from './reducers/TabbarReducer';
import CityReducer from './reducers/CityReducer';
import CinemaListReducer from './reducers/CinemaListReducer';

//redux-thunk、redux-promises是redux中间件,用来处理异步的action creator
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

//combineReducers合并reducer
const reducer = combineReducers({
  TabbarReducer,
  CityReducer,
  CinemaListReducer
});
//compose-redux浏览器调试工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,composeEnhancers(applyMiddleware(reduxThunk,reduxPromise)));

export default store;

3.1.1 creareStore原理

function createStore(reducer){
  let list = [];
  let state = reducer(undefined,{});//传参undefined函数也会使用默认值

  function subscribe(callback){
    list.push(callback);
  }

  function dispatch(action){
    state = reducer(state,action);

    list.forEach(cb=>{
      cb && cb()
    });
  }

  function getState(){
    return state
  }

  return {
    subscribe,
    dispatch,
    getState
  }
}

3.2 reducers内文件

TabbarReducer.js

// 定义一个状态
let initialState = {
  show: true
};
 
// 利用reducer将store和action串联起来
function reducer(prevState = initialState, action) {
  let newState = {...prevState};
  switch (action.type) {
    case 'hidden-tabbar':
      newState.show = action.show;
      return newState;
    case 'show-tabbar':
      newState.show = action.show;
      return newState;
    default:
      return prevState;
  }
}
 
export default reducer;

3.3 actionCreator

3.3.1 同步-TabbarAction.js

function show(){
  return {
    type:'show-tabbar',
    show:true
  }
}
function hidden(){
  return {
    type:'hidden-tabbar',
    show:false
  }
}
export {show,hidden}  

export default 和 export的区别

3.3.2 异步-CinemaListAction.js

import axios from 'axios'

//redux-thunk
function getCinemaList(){
  return (dispatch) =>{
    axios({
      url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
      method:'get',
      headers:{
        'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
        'X-Host': 'mall.film-ticket.cinema.list'
      }
    }).then(res=>{
      dispatch({
        type:'change-cinemaList',
        cinemaList:res.data.data.cinemas
      })
    })
  }
}

//redux-promise
// function getCinemaList(){
//   return axios({
//     url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
//     method:'get',
//     headers:{
//       'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
//       'X-Host': 'mall.film-ticket.cinema.list'
//     }
//   }).then(res=>{
//     return {
//       type:'change-cinemaList',
//       cinemaList:res.data.data.cinemas
//     }
//   })
// }

//ES7  async await 依次执行异步,全部执行完,再执行最后的代码
// async function getCinemaList(){
//    let action = await axios({
//     url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
//     method:'get',
//     headers:{
//       'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
//       'X-Host': 'mall.film-ticket.cinema.list'
//     }
//   }).then(res=>{
//     return {
//       type:'change-cinemaList',
//       cinemaList:res.data.data.cinemas
//     }

//   })
//   return action
// }

export default getCinemaList;

ES7 async await

4. store api应用

  • subscribe-订阅
  • dispatch-事件分发
  • getstate-获取状态

注意:不管是任意的的一个dispatch都会调用应用中的全部订阅,所以在组件销毁的时候要取消订阅,不过在使用react-redux就不用担心,react-redux会帮忙处理好订阅和取消订阅。

4.1 class组件-Cinemas.js

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

export default class Cinemas extends Component {
  state = {
    cityName:store.getState().CityReducer.cityName,
    cinemaList:store.getState().CinemaListReducer.cinemaList
  };
  componentDidMount(){
    this.unsubscribe = store.subscribe(()=>{
      // console.log('cinemas-订阅',this.state.cinemaList)
      this.setState({cinemaList:store.getState().CinemaListReducer.cinemaList})
    });

    if(store.getState().CinemaListReducer.cinemaList.length === 0){
      store.dispatch(getCinemaList());
    };
  }
  componentWillUnmount(){
    //取消订阅
    this.unsubscribe();
  }
  render() {
    return (
      <div>
        <div style={{overflow:'hidden'}}>
          <div style={{float:'left'}} onClick={()=>{
            this.props.history.push('/city');
            }}>
            {this.state.cityName}
          </div>
          <div style={{float:'right'}} onClick={()=>{
            this.props.history.push('/cinemas/search');
            }}>
            搜索
          </div>
        </div>
        {this.state.cinemaList.map((item) =>  {
          return (
            <dl key={item.cinemaId} style={{padding:'10px'}}>
              <dt>{item.name}</dt>
              <dd style={{fontSize:'12px',color:'gray'}}>{item.address}</dd>
            </dl>
          )
        })}
      </div>
    )
  }
}

4.2 函数组件-Search.js

import React,{useState,useEffect,useMemo}from 'react'
import store from '../redux/store'
import getCinemaList from '../redux/actionCreator/CinemaListAction'

export default function Search() {
  const [cinemaList,setCinemaList] = useState(store.getState().CinemaListReducer.cinemaList);
  const [text,setText] = useState('');

  useEffect(()=>{
    let unsubscribe = store.subscribe(()=>{
      setCinemaList(store.getState().CinemaListReducer.cinemaList)
    });

    if(store.getState().CinemaListReducer.cinemaList.length === 0){
      store.dispatch(getCinemaList());
    };
    return ()=>{
      //取消订阅
      unsubscribe();
    }
  },[])

  const getList = useMemo(() => cinemaList.filter(item =>{
    return item.name.toUpperCase().includes(text.toUpperCase()) || item.name.toLowerCase().includes(text.toLowerCase())
  }),[cinemaList,text]);

  return (
    <div>
      <input value={text} onChange={(evt)=>{
        setText(evt.target.value)
      }}></input>
      <ul>
        {getList.map((item) =>  {
          return (
            <dl key={item.cinemaId} style={{padding:'10px'}}>
              <dt>{item.name}</dt>
              <dd style={{fontSize:'12px',color:'gray'}}>{item.address}</dd>
            </dl>
          )
        })}
      </ul>
    </div>
  )
}

5. 调试工具redux-devtools-extension

5.1 安装redux-devtools-extension

浏览器扩展程序下载地址

5.1.1下载chrome可用版本

在这里插入图片描述

5.1.2 安装到chrome浏览器中

浏览器右上角三点=>更多工具=>扩展程序=>开发者模式
将安装包拖入
在这里插入图片描述

5.2 store.js中进行配置

引入compose
配置内容在上述的store.js中

5.3 使用redux-devtools-extension

如果不能使用,重启浏览器

在这里插入图片描述

6. 数据持久化react-redux

6.1 安装相关包

npm i react-redux
npm i redux-persist //数据持久化包

6.2 store.js改写

import {createStore,combineReducers, applyMiddleware,compose} from 'redux';
import TabbarReducer from './reducers/TabbarReducer';
import CityReducer from './reducers/CityReducer';
import CinemaListReducer from './reducers/CinemaListReducer';

//redux-thunk、redux-promises是redux中间件,用来处理异步的action creator
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

//数据持久化
import {persistStore,persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage';

//数据持久化配置项
const persistConfig = {
  key:'redux',
  storage:storage,
  //localStorage: import storage from 'redux-persist/lib/storage'
  //sessionStorage: import storageSession from 'redux-persist/lib/storage/session'
  whitelist:['CityReducer'],//白名单只保存CityReducer
  // blacklist:['CityReducer'],//黑名单仅不保存CityReducer
}
//combineReducers合并reducer
const reducer = combineReducers({
  TabbarReducer,
  CityReducer,
  CinemaListReducer
});
//被数据持久化的reducer
const persistedReducer = persistReducer(persistConfig,reducer);

//compose-redux浏览器调试工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  persistedReducer,
  composeEnhancers(applyMiddleware(reduxThunk,reduxPromise))
);
//被数据持久化的store
const persistor = persistStore(store);

export {store,persistor};

6.3 根组件改写

  • React-Redux 提供Provider组件,可以让容器组件拿到state。
  • Provider组件,可以让容器组件拿到state , 使用了context
import React from "react";
import ReactDOM from "react-dom/client";
// import ReactDOM from "react-dom";
import App from './06-react-redux/App'
import {store,persistor} from './06-react-redux/redux/store'
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";

// ReactDOM.render(
//   // <React.StrictMode>
//   <Provider store={store}>
//     <PersistGate loading={null} persistor={persistor}>
//       <App />
//     </PersistGate>
//   </Provider>
//   // </React.StrictMode>
//   ,document.getElementById('root'))

//v18
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  // <React.StrictMode>
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>
  // </React.StrictMode>
);

6.4 改写使用subscribe和dispatch

  • React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这
    两种组件连起来。
  • connect 是HOC(Higher-order component), 高阶组件

6.4.1 UI组件 和 容器组件

UI组件 和 容器组件
(1) UI组件

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 所有数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

(2) 容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 AP

6.4.2 应用到组件

6.4.2.1 Cinemas.js

connect(将来传给孩子的属性,将来给孩子传的回调函数)(原组件)

import React, { useEffect } from 'react'
import { connect } from 'react-redux'
import getCinemaList from '../redux/actionCreator/CinemaListAction'

function Cinemas(props) {
  let {cinemaList,getCinemaList,cityName} = props;
  useEffect(()=>{
    if(cinemaList.length === 0){
      getCinemaList();
    };
  },[cinemaList,getCinemaList]);

  return (
    <div>
      <div style={{overflow:'hidden'}}>
        <div style={{float:'left'}} onClick={()=>{
          props.history.push('/city');
          }}>
          {cityName}
        </div>
        <div style={{float:'right'}} onClick={()=>{
          props.history.push('/cinemas/search');
          }}>
          搜索
        </div>
      </div>
      {cinemaList.map((item) =>  {
        return (
          <dl key={item.cinemaId} style={{padding:'10px'}}>
            <dt>{item.name}</dt>
            <dd style={{fontSize:'12px',color:'gray'}}>{item.address}</dd>
          </dl>
        )
      })}
    </div>
  )
}
//connect(将来传给孩子的属性,将来给孩子传的回调函数)(原组件)
const mapStateToProps = (state) => {
  return {
    cityName:state.CityReducer.cityName,
    cinemaList:state.CinemaListReducer.cinemaList
  }
};
const mapDispatchToProps ={
  getCinemaList
};
export default connect(mapStateToProps,mapDispatchToProps)(Cinemas);

6.4.2.2 City.js

connect(将来传给孩子的属性,将来给孩子传的回调函数)(原组件)

import React,{useState} from 'react'
import { connect } from 'react-redux'

function City(props) {
  const [cityList] = useState(['广东','深圳','杭州','上海']);

  return (
    <div>
      <ul>
        {
          cityList.map(item=>{
            return <li key={item} onClick={()=>{
              props.changeCity(item);
              // props.history.push('/cinemas')
              props.history.goBack();
            }}>
              {item}
            </li>
          })
        }
      </ul>
    </div>
  )
}

const mapDispatchToProps ={
  changeCity(item){
    return {
      type:'change-city',
      cityName:item
    }
  }
};
export default connect(null,mapDispatchToProps)(City);

6.5 类似connect的高阶组件原理

import React, { Component } from 'react'

class NotFound extends Component {
  componentDidMount(){
    console.log(this.props)
  }

  render() {
    return (
      <div>
        404 NotFound
      </div>
    )
  }
}

function myconnect(cb,obj){
  if(cb){
    var state = cb();
  };
  
  return (MyComponent)=>{//return 一个处理组件的函数
    return (props)=>{//return 一个函数组件
      return (//return 函数组件中返回具体的组件内容
        <div style={{color:'red'}}>
          {/* 传递route中的props.history等属性、state和obj */}
          <MyComponent {...props} {...state} {...obj}/>
        </div>
      )
    }
  }
}

const mapStateToProps = (state) => {
  return {
    a:1,
    b:2
  }
};
const mapDispatchToProps ={
  show(){

  },
  hide(){

  }
};
export default myconnect(mapStateToProps,mapDispatchToProps)(NotFound);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Redux中实现数据持久化有多种方法,下面是其中一种常用的方法: 1. 使用redux-persist库:redux-persist是一个用于实现Redux数据持久化的第三方库。它通过将Redux store中的数据保存到本地存储(如localStorage或sessionStorage)中,以便在刷新页面或重新加载应用程序时保持数据的持久性。 首先,安装redux-persist库: ``` npm install redux-persist ``` 然后,在Redux的配置文件中,进行redux-persist的配置和初始化: ```javascript import { createStore } from 'redux'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; // 默认使用localStorage // 导入你的reducer import rootReducer from './reducers'; // 配置redux-persist const persistConfig = { key: 'root', // 存储的key,默认为root storage, // 使用的存储引擎,默认为localStorage }; const persistedReducer = persistReducer(persistConfig, rootReducer); // 创建store const store = createStore(persistedReducer); const persistor = persistStore(store); export { store, persistor }; ``` 最后,在应用程序的入口文件中,使用`PersistGate`组件包裹整个应用程序,并将`persistor`作为其属性: ```javascript import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { store, persistor } from './store'; ReactDOM.render( <Provider store={store}> <PersistGate loading={null} persistor={persistor}> // 应用程序的根组件 </PersistGate> </Provider>, document.getElementById('root') ); ``` 使用以上配置,Redux的状态将会被自动保存到本地存储中,并在应用程序重新加载时被恢复。你可以根据需要自行调整配置,例如设置存储引擎、存储的key等。详细的配置和更多高级用法,请参考redux-persist库的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值