网络建设 之 React数据管理

这篇具有很好参考价值的文章主要介绍了网络建设 之 React数据管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

React作为一个用于构建用户界面的JavaScript库,很多人认为React仅仅只是一个UI 库,而不是一个前端框架,因为它在数据管理上是缺失的。在做一个小项目的时候,维护的数据量不多,管理/维护数据用useState/useRef就足够了;可是当项目变大,需要的数据量成百上千,然后就会发现:

  1. 全局变量到处都是。

  2. 在某些组件里定义的数据无法传递到其他组件里。

  3. 数据传来传去找不到定义位置,很难维护。

因此这时候就需要数据管理了。

最简单的数据管理

就是把这些useState/useRef定义的数据放到根组件上,然后哪个子组件用,就用props传下去,这样没有其他概念浅显易懂,也起到了一定的数据管理的作用。但这样做的缺点就是这些数据需要在子组件一层层的传下去,代码要写很多,比较麻烦,如果不嫌麻烦的话,在大型项目里,这么做其实也没什么问题了。

更进一步的数据管理,用useContext

React的api,useContext,正是为了解决数据层层传递的问题而出现的,它可以看作是一个数据中心,所有需要管理的数据都在这里。

它怎么用呢,首先新开一个文件context.js,在里用React.createContext()定义一个Context然后导出:

//context.js
import React from "react";
export const Context = React.createContext();

然后在根节点这里,用这个Context的Provider属性将整个根节点包裹住:

// rootView.jsx
import React from "react";
import { Context } from "./context";

export default function RootView() {
	const defaultValue = {a: 1, b: 'hello'};
	return <Context.Provider value={defaultValue}>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </Context.Provider>;
}

这里的defaultValue就是我们数据中心的所有数据的初始化的默认值。

然后在子组件里,不管是子组件还是孙组件还是孙孙组件,都不用再把 props 当传家宝传下去了,只需要在组件里像useState一样调用useContext,就能获取到数据中心的所有数据:

//child.jsx
import React, { useContext } from "react";
import { Context } from "./context";

export default function() {
	const state = useContext(Context);
	return <Text>{state.b}</Text>
}

state就是数据中心的所有数据,可以理解为useState中的State,这样这个子组件显示的就是上面默认的初始化数据“hello”,但这还不够好用,因为目前还没有办法改变数据,那么我们接下来就需要对defaultValue做一些变动,把这些数据都用useState变成响应式的,然后再一股脑地传进Provider的value里:

// rootView.jsx
import React from "react";
import { Context } from "./context";

export default function RootView() {
	const [value, setValue] = useState({a: 1, b: 'hello'});
	return <Context.Provider value={{value, setValue}}>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </Context.Provider>;
}

然后在子组件里这样调用:

//child.jsx
import React, { useContext } from "react";
import { Context } from "./context";

export default function() {
	const state = useContext(Context);
  useEffect(()=>{
  	state.setValue({...state.value, b: 'world'});
  }, []);
	return <Text>{state.value.b}</Text>
}

然后,这个子组件显示的就是已经改动的数据“world”,关于Context还有一个比较重要的点是:当Context Provider的value发生变化时,他的所有调用useContext的子组件,都会重新渲染,这往往会造成比较严重的性能问题,在大型项目里百分百会出现。

第一个问题是state改变,造成Provider标签下的整体渲染。Context.Provider说到底还是组件,也是用React.createElement()实现的,也按照组件基本法来办事,React.createElement()在每次props发生变动时,都会创建一个新对象,那么只要让props不发生变动就行了。我给Provider再包裹一层ProviderWrapper,然后在这个ProviderWrapper组件里去定义数据,这样,由于ProviderWrapper是不变的,那么在RootView组件里没有任何状态改变,子组件也用不着重复渲染了。

const ProviderWrapper = ({ children }) => {
  const [value, setValue] =useState(defaultValue); 
  return (
    <Context.Provider value={{ value, setValue }}>
    	{children}
    </Context.Provider>
  );
};

export default function RootView() {
	return <ProviderWrapper>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </ProviderWrapper>;
}

这样,babel在编译的时候,标签转译成React.createElement()的时候,只是在RootView组件里完成转译,React.createElement()执行完的节点数据将通过props.children传入ProviderWrapper,在ProviderWrapper内部就没有重复的React.createElement(),这样就避免了整体的重复渲染。

二是上述的所有调用useContext的子组件的局部重复渲染。即便在某一个子组件中只是使用了setState,并没有使用state,但是当state变动时,这个子组件仍然会重复渲染,因为仅仅是调用了useContext,但理论上来说是不需要重复渲染的。那解决办法是什么呢?解决办法就是将state和setState分别用不同的Provider传入,这样一个组件仅仅只是调用setState的话,就不会被state的变动影响而重复渲染:

const ProviderWrapper = ({ children }) => {
  const [value, setValue] =useState(defaultValue); 
  return (
    <SetValueContext.Provider value={{ setValue }}>
      <ValueContext.Provider value={{ value }}>
      	{children}
      </Context.Provider>
    </Context.Provider>
  );
};

其中SetValueContext和ValueContext是两个毫不相干的有React.createContext()产生的对象,仅仅只是用来区分开state和setState,这样在子组件里,如果只想调用setState,那么就通过React.useContext()引入SetValueContext即可,子组件就不会因state变动而重复渲染。

这样基本上就差不多了,难懂的代码多了一些,但冗余的代码少了不少。概念越多就能解决的更多的问题,现在又出现了一个问题,state里有很多数据,一些子组件引用了React.useContext(),但是对state里的一些数据是不关心用不到的,但这些数据在发生变动的时候,这些子组件也会重复渲染,说白了,就是state细粒度不够的问题,但是本着尽可能消除重复渲染的思想,我们把state根据数据种类进行拆分成多个state,这样每个子组件调用对自己有用的state,这样就减少了重复渲染:

const ProviderWrappers = ({ children }) => (
  <LoginProviderWrapper>
    <SignupProviderWrapper>
      <MainPageProviderWrapper>
        <MenuProviderWrapper>
          {children}
        </MenuProviderWrapper>
      </MainPageProviderWrapper>
    </SignupProviderWrapper>
  </LoginProviderWrapper>
);

export default function RootView() {
	return <ProviderWrappers>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </ProviderWrappers>;
}

等一下,代码怎么变冗余了?我们最初的目的是什么?消除冗余,我们为了消除一种冗余,带来了另一种冗余,这是不可接受的,所以还得接着改,当前情况是,由于state被拆分,造成出现了很多ProviderWrapper支持不同的state和setState,那么我们需要对这些ProviderWrapper进行某种程度上的组合,至少我们可以用一个for循环去组合这些ProviderWrapper:

// RootView.tsx
function composeProviderWrappers(ProviderWrappers) {
	const element;
  for(ProviderWrapper of ProviderWrappers) {
  	element = <ProviderWrapper>{element}</ProviderWrapper>
  }
  return element;
}

export default function RootView() {
	const ComposeProviderWrappers = composeProviderWrappers([LoginProviderWrapper, SignupProviderWrapper, MainPageProviderWrapper, MenuProviderWrapper]);
	return <ComposeProviderWrappers>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </ComposeProviderWrappers>;
}

这个优化意义不大,并没有减少多少冗余代码,但是说实话,我们现在已经走歪了,而导致我们走歪的罪魁祸首,就是React.useContext()的性能问题:只要调用React.useContext()的组件,当state变动的时候,全部都会重新渲染。回到最开始说的,React相对于Framwork,其实它更类似于一个UI库,用React本身的功能勉强实现数据管理,代价就是有很多坑,毕竟使用一些第三方数据管理库例如Redux,zustand之类的,既能实现React.useContext()的功能,又能避免React.useContext()的问题,何乐而不为呢?下面就来介绍一些第三方数据管理库:

Redux

Redux可以说是最正统的React数据管理工具,Redux的用法与React.useContext()类似,但没有React.useContext()的缺点,只有组件在使用到变动的数据的时候,这个组件才会重新渲染,如果你在因使用React.useContext()导致的无限渲染大卡关时,不妨试试Redux。

Redux只有2KB,Redux Toolkit是官方推荐的编写 Redux 逻辑的方法,使编写 Redux 更加容易。安装方式如下:

# NPM
npm install @reduxjs/toolkit redux

# Yarn
yarn add @reduxjs/toolkit redux

使用时,首先像React.createContext()一样,使用configureStore导出一个实例:

import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: {}
})

然后用react-redux提供的Provider标签,将整个根节点包裹起来,唯一的区别就是,我们再也不用考虑担心性能问题了,这里不会有的:

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

export default function RootView() {
	return <Provider store={store}>
  	<View class='root-view'>
    	...各种子组件...
    </View>
   </Provider>;
}

然后不一样的来了,创建slice:

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment: state => {
      // Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
      // 并不是真正的改变状态值,因为它使用了 Immer 库
      // 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
      // 不可变的状态
      state.value += 1
    },
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
})
// 每个 case reducer 函数会生成对应的 Action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

这里的createSlice实际上可以考虑为创建state和setState,reducers就是setState。然后将 Slice Reducers 添加到 Store 中:

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
  reducer: {
    counter: counterReducer
  }
})

最后就是使用了,在 React 组件中使用 Redux 状态和操作:

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
import styles from './Counter.module.css'

export function Counter() {
  const count = useSelector(state => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

虽然起的名字不同,但是通过上述的React.useContext()的学习,基本上也是能一一对应的,最重要的是,这里不会再有性能问题了。

zustand

"Zustand" 只是德语的"state",一个轻量,现代的状态管理库,它的好处就是更简单。

安装:

npm install zustand

然后老生常谈的定义一个实例:

const useStore = create(set => ({
  votes: 0,
  addVotes: () => set(state => ({ votes: state.votes + 1 })),
  subtractVotes: () => set(state => ({ votes: state.votes - 1 })),
}));

然后,就可以使用了,这个真的比较方便:

function App() {
  const addVotes = useStore(state => state.addVotes);
  const subtractVotes = useStore(state => state.subtractVotes);
  
  return <div className="App">
      <h1>{getVotes} people have cast their votes</h1>
      <button onClick={addVotes}>Cast a vote</button>
      <button onClick={subtractVotes}>Delete a vote</button>
  </div>
}

Rematch

Rematch在Redux的基础上构建并减少了样板代码和执行了一些最佳实践。Redux对于初学者来说简直就是噩梦,他仿佛不是一个状态管理工具,而是一个涉及了众多概念的状态管理模型。要想搞明白Redux如何使用,就要先了解10个以上名词的含义;这还只是Redux的主流程使用中涉及到的名词。Redux的主流程里充斥了各种各样的概念,比如,Dispatch、Reducer、CreateStore、ApplyMiddleware、Compose、CombineReducers、Action、ActionCreator、Action Type、Action Payload、BindActionCreators...Rematch将这些概念进行了整合,提出了一个更简洁的状态管理模型;

安装:

npm install @rematch/core react-redux

首先,定义一个实例:

import { init } from "@rematch/core";

// 定义一个model,包含了之前redux中的一些内容
// 拥有对应的state和reducers

//model
const count = {
  state: 0,
  reducers: {
    upBy: (state, payload) => state + payload,
  },
};
// 使用init初始化
// 相当于Redux中的store
init({
  models: { count },
});

然后,就可以使用了:

import { connect } from "react-redux";

// Component
// 将count内容赋值给count
const mapStateToProps = (state) => ({
  count: state.count,
});
// 将指定动作传输给组件
const mapDispatchToProps = (dispatch) => ({
  countUpBy: dispatch.count.upBy,
});

connect(mapStateToProps, mapDispatchToProps)(Component);
// connect倒是没有怎么变

jotai,recoil,redux,rematch,zustand,Reducer,react数据管理的哲学文章来源地址https://www.toymoban.com/news/detail-727347.html

到了这里,关于网络建设 之 React数据管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React Dva项目创建Model,并演示数据管理与函数调用

    本文的话 我们讲一下定义Model 也就是Dva中redux的部分 我们打开一个刚创建的Dva项目 看到 src下的models 下 就是Model部分 这里 他给我们了一个案例 如果用 react-redux 管理 模块多了之后会看着比较乱 或 很麻烦 但是 大家会发现 在Model中 他将这些都放在一起了 只需要创建一个这样

    2024年02月15日
    浏览(40)
  • 医院网络安全建设:三网整体设计和云数据中心架构设计

    医院网络安全问题涉及到医院日常管理多个方面,一旦医院信息管理系统在正常运行过程中受到外部恶意攻击,或者出现意外中断等情况,都会造成海量医疗数据信息的丢失。由于医院信息管理系统中存储了大量患者个人信息和治疗方案信息等,如果这些数据信息遭到篡改很

    2024年01月18日
    浏览(82)
  • 【基础建设】浅谈企业网络安全运营体系建设

    引言 在网络安全环境复杂又严峻的当前,国内各大企业已开始组建自己的网络安全团队,加强企业自身安全能力建设,朝着网络安全运营一体化迈进。但企业安全运营也已逐步从被动式转变为主动式,成为将人、管理与技术结合,全面覆盖网络安全监测、预警、防护、检测、

    2024年02月09日
    浏览(50)
  • 工控网络安全分支-电力行业网络安全建设

    长期以来,传统工业系统的设备专有性与天然隔离性使得人们忽视了信息安全隐患的存在,管理者与工程师们往往将安全关注的焦点和资金预算都投放在设备安全和生产安全方面,预防发生工业事故造成人员、财产、或环境损失。然而,信息技术的发展已经打破了传统的“物

    2024年02月03日
    浏览(72)
  • 5G网络建设--并查集--最小生成树

    题目描述 现需要在某城市进行5G网络建设,已经选取N个地点设置5G基站,编号固定为1到N,接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通,不同基站之间假设光纤的成本各不相同,且有些节点之间已经存在光纤相连。 请你设计算法,计算出能联通这些基站

    2024年04月17日
    浏览(53)
  • 银行网络安全实战对抗体系建设实践

    党的十八大以来,将网络安全提升到前所未有的新高度,银行牢牢把握国家网络安全战略目标,已加强自身建设,建立了较为完善的安全防护体系。同时随着国际网络安全攻防对抗升级,银行转变思路、主动作为,从被动防守向主动防御、动态防御转型,聚焦传统攻防演练的

    2024年01月21日
    浏览(58)
  • 网络综合布线实训室建设方案

    网络综合布线系统是为了满足数据通信需求而设计和建立的一套基础设施。它提供了数据传输、信号传输和电力供应的基础结构,支持各种网络设备和终端设备之间的连接。 网络综合布线系统通常包括以下组成部分: 1) 数据通信线缆:网络综合布线系统使用各种类型的通信

    2024年02月12日
    浏览(55)
  • 【华为OD机试】5G网络建设【C卷|200分】

    【 华为OD机试】-真题 !!点这里!! 【 华为OD机试】真题考点分类 !!点这里  !! 题目描述 现需要在某城市进行5G网络建设,已经选取N个地点设置5G基站,编号固定为1到N, 接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通,不同基站之间假设光纤的成本各不相

    2024年04月23日
    浏览(44)
  • 网络安全:数字中国建设和发展的基石

    数字中国的概念已经深入人心,随着互联网技术的快速发展,我们的生活已经离不开数字技术。然而,在享受数字技术带来的便利的同时,我们也面临着越来越多的网络安全威胁。网络安全不仅关系到个人信息的安全,更关系到国家安全和社会稳定。 网络安全是指通过采取必

    2024年02月04日
    浏览(50)
  • 洞悉安全现状,建设网络安全防护新体系

    一、“网络攻防演练行动“介绍 国家在2016年发布《网络安全法》,出台网络安全攻防演练相关规定:关键信息基础设施的运营者应“制定网络安全事件应急预案,并定期进行演练”。同年“实战化网络攻防演练行动”成为惯例。由公安部牵头,每年举办一次,针对全国范围

    2024年02月14日
    浏览(72)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包