• WEB3 在 React搭建的Dapp中通过redux全局获取并存储用户ETH与自定义token与交易所存储数量


    上文 web3 在React dapp中全局管理web3当前登录用户/智能合约等信息中 我们简单操作,将web3的公共信息都存在了window对象上

    然后 我们先来启动一下环境
    终端输入

    ganache -d
    
    • 1

    在这里插入图片描述
    打开项目 终端输入

    truffle migrate --reset
    
    • 1

    在区块链上发布一下智能合约
    在这里插入图片描述

    然后 我们在 src下的 components 目录下创建两个组件
    分别是 余额组件 balance.jsx 订单组件 Order.jsx
    在这里插入图片描述
    然后 在我们 src下的view下的index.jsx组件中引入他们

    我们这里 直接导入 然后使用他们
    在这里插入图片描述
    那么 我们余额的管理 显然是不能用组件传值的 因为他要动态的去变动
    redux就是一个非常不错的选择

    那么 我们终端执行

    npm i --save redux react-redux
    
    • 1

    引入一下依赖
    在这里插入图片描述
    然后 我们在src目录下创建文件夹 redux 下面创建一个 store.js
    然后 我们还需要一个依赖
    终端输入

    npm i --save @reduxjs/toolkit
    
    • 1

    在这里插入图片描述
    然后 我们在src下的redux 目录中创建一个文件夹目录叫 balanceSlice 下面创建一个index.js

    参考代码如下

    import {createSlice} from "@reduxjs/toolkit";
    
    const balanceSlice = createSlice({
        name:"balance",
        initialState: {
          TokenWallet: "0", //用户自定义token的数量 因为要进行单位转换 所以 用了字符串类型
          TokenExchange: "0", //用户在交易所的token数量  依旧采取字符串类型
          EtherWallet: "0",  //用户的ETH值
          EtherExchange: "0"  //用户在交易所的ETH
        },
        reducers: {
            setTokenWallet(state,action) {
                state.TokenWallet = action.payload
            },
            setTokenExchange(state,action) {
                state.TokenExchange = action.payload
            },
            setEtherWallet(state,action) {
                state.EtherWallet = action.payload
            },
            setEtherExchange(state,action) {
                state.EtherExchange = action.payload
            }
        }
    })
    
    export const { setTokenWallet, setTokenExchange, setEtherWallet, setEtherExchange } = balanceSlice.actions;
    
    export default balanceSlice.reducer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这里 我们存储了当前登录用户的token与ETH 以及 当前用户放在交易所的 token与 ETH
    然后 写了更改这些数据的set方法
    最后 将我们的数据与几个set函数都导出去

    然后 我们src下的 redux 下的 store.js 编写代码如下

    import { configureStore } from "@reduxjs/toolkit";
    import balanceReducer from "./balanceSlice";
    
    const store = configureStore({
      reducer: {
        balance: balanceReducer
      }
    });
    
    export default store;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里 我们导入 自己刚才写的 balanceSlice下的index 配置store

    然后 我们找到 src目录下的 App.js 组件 编写代码如下

    import React from 'react';
    import Router from "./router/index";
    import {Provider} from "react-redux"
    import store from "./redux/store";
    
    export default function App() {
      return (
        <Provider store={store}>
          <Router />
        </Provider>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其实关键就在于 通过react-redux 导入Provider组件 嵌套我们的路由组件 让所有的路由组件都能享受到共享的数据资源
    然后这个数据资源来自 我们配置的redux/store

    然后 src目录下的 components目录下 Order.jsx
    这里 我们保证这个组件格式不要有问题就好

    import React from 'react';
    
    export default function Order() {
    
      return (
        <div>
          订单组件
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后 我们在 src下的 components 下的 balance.jsx
    编写代码如下

    import React from 'react';
    import { useSelector, useDispatch } from "react-redux";
    import { setTokenWallet } from "../redux/balanceSlice";
    
    export default function Balance() {
      const state = useSelector((state) => state.balance.TokenWallet);
      const dispatch = useDispatch()
      return (
        <div>
          测试组件{state}
          <button onClick={()=>{
            dispatch(setTokenWallet("1000"))
          }}>修改一下</button>
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这里 我们拿取了 TokenWallet 赋值给了state
    然后 当按钮点击 我们调用 setTokenWallet修改TokenWallet值的函数
    然后 我们启动项目

    这里 我们项目运行起来 会看到 TokenWallet 的展示是完全OK的 一个0
    在这里插入图片描述
    然后我们点击修改一下的按钮 可以看到 setTokenWallet 已经成功修改了TokenWallet的值
    在这里插入图片描述
    好啦 那么 既然已经试过是可以用的了
    我们 src下 components 下的 balance.jsx 余额组件 先改回一个正常的格式

    import React from 'react';
    //import { useSelector, useDispatch } from "react-redux";
    
    export default function Balance() {
      //const state = useSelector((state) => state.balance.TokenWallet);
      return (
        <div>
          余额组件
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    然后 我们要做的是 在项目初始化时 就拿到用户的这些信息
    那么 在哪里拿?
    我们打开 src下的 redux下面的 balanceSlice 下的 index.js文件
    在这里多引入一个依赖
    在这里插入图片描述
    @reduxjs/toolkit 下的 createAsyncThunk
    用它来编写异步函数
    然后 我们在最下面 这样写

    这里 我们导出了一个异步函数 叫 loadBalanceData

    然后 balance/fetchBalanceData是这个函数会生成的一个名字 一定要规范一点
    然后 我们函数接受两个参数 第一个 是一个data数据对象 然后第二个 我们接受一个对象 里面必须有一个叫dispatch的字段 他是一个回调函数
    简单说 这边异步的逻辑完成了 通过 dispatch 回调回去

    export const loadBalanceData = createAsyncThunk(
        "balance/fetchBalanceData",
        async (data, {dispatch}) => {
            console.log(data)
        }
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    逻辑的话 我们先写的比较简单 就把data打印出来看了一下

    然后问题来了 我们要在哪里调用呢?
    没错 src下的 view下的 index组件
    在这里插入图片描述
    我们就要在他刚拿完WEB3信息的这个地方去调用 然后 data数据 就是要传这个web3 信息进去

    我们直接改写代码如下

    import { useEffect } from 'react';
    import Web3 from "web3";
    import grToken from "../build/grToken.json";
    import Exchange from "../build/Exchange.json";
    import Balance from "../components/balance";
    import Orber from "../components/Order";
    import { useDispatch } from "react-redux";
    import { loadBalanceData } from "../redux/balanceSlice";
    export default function PageIndex() {
      const dispatch = useDispatch()
      useEffect(() =>{
          async function start(){
              const WebData = await initialization()
              window.WebData = WebData;
              dispatch(loadBalanceData(WebData))
          }
          start();
      },[dispatch])
    
      // 获取web 信息
      async function initialization() {
          var web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
          let account = await web3.eth.requestAccounts();
          let networkId = await web3.eth.net.getId();
          const token = await new web3.eth.Contract(grToken.abi,grToken.networks[networkId].address);
          const etoken = await new web3.eth.Contract(Exchange.abi,Exchange.networks[networkId].address);
          return {
            web3,
            account: account[0],
            grToken: token,
            Exchange: etoken
          }
      }
      return (
        <div>
          <Balance></Balance>
          <Orber></Orber>
          欢迎来到 Web3 练习的世界
        </div>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    做了部分语法改动 但其实说到底 唯一的改变就是 在initialization返回web3 信息成功后
    我们调用了loadBalanceData 并传入了 我们刚获取到的web3 信息

    然后 我们loadBalanceData其实也就输出了一下这个传入的data数据
    在这里插入图片描述
    运行代码 查看控制台 也看得到 他是成功将我们的这个web3对象成功的输出了出来
    在这里插入图片描述
    具体 要怎么获取到我们当前用户的grtoken 我们还是要看看 之前自己写的合约

    打开 grtoken的合约 我们会发现 当年我们写了个对象 balanceOf
    只需要提供当前账号的地址 就会返回对应的toekn
    在这里插入图片描述
    我们直接将src下redux 下 balanceSlice下的inex 中的 loadBalanceData代码更改如下

    export const loadBalanceData = createAsyncThunk(
        "balance/fetchBalanceData",
        async (data, {dispatch}) => {
            //从data中将web3信息都结构出来
            const {
                web3,
                account,
                grToken,
                Exchange
            } = data;
            //用户当前用户token信息
            const TokenWallet = await grToken.methods.balanceOf(account).call()
            console.log(TokenWallet)
           //获取当前用户在交易所的token信息
    
        }
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里 我们通过grToken合约的balanceof函数 传入account当前用户 去获取当前用户的grtoken信息
    这里注意要用 call 告诉他们操作不上链 不消耗燃料啊 这个很关键
    然后我们拿到 控制台输出一下

    我们运行代码
    在这里插入图片描述
    可以看到 我们用户的token 信息 就这样获取出来了 但是这个单位明显是有的问题的 我们要转换一下
    但保持最小状态 是利于全局管理的 所以 我们是要转换 但绝对不是在这里转换 这里要存储数据 我们在redux中自然是要保持最小单位的数据管理

    最后 等他获取成功了 我们调用 setTokenWallet让他把数据 赋值给我们全局的TokenWallet做管理
    在这里插入图片描述
    然后是交易所中的 grtoken
    这里 我们还是要去看自己之前交易所合约
    这里 我们也写了个 balanceof 需要传入当前要查的是哪一种token的地址 和 当前用户的地址
    在这里插入图片描述
    但是这个要注意下 o的大小写 这个也是我之前留的坑了
    我们这里这样写

    const TokenExchange = await Exchange.methods.balanceof(grToken.options.address,account).call()
    console.log(TokenExchange)
    
    • 1
    • 2

    通过Exchange交易所 合约 调用他的balanceof函数 这里 我们通过grToken合约对象 拿到他的地址 然后传入当前账号的地址
    最后得到token信息
    然后在控制台输出
    在这里插入图片描述
    运行结果如下
    在这里插入图片描述
    啊 0 没毛病啊
    我们并没有往交易所中存储grtoken 所以是没什么问题的

    好 那 这里 我们获取到了 就赋值回调一下
    在这里插入图片描述
    然后 获取用户当前的ETH 这个就比较简单了 我们很早就演示过
    直接编写代码如下

    const EtherWallet = await web3.eth.getBalance(account)
    console.log(EtherWallet)
    
    • 1
    • 2

    直接用web3 对象的getBalance 然后传入当前账号就可以了
    运行代码 查看看着他打印
    在这里插入图片描述
    没有任何问题 还是一个最小单位 没有转换过的
    在这里插入图片描述
    然后 获取到之后 我们还是一样做个数据的写入
    在这里插入图片描述
    最后一个 获取在交易所中的 ETH

    ETH的地址 我们之前写脚本的时候用过
    在这里插入图片描述

    const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
    
    • 1

    我们直接将这个拿过来
    写到我们 src下的 redux下的balanceSlice下的 index中
    在这里插入图片描述
    然后 我们最下面直接这样写

    //获取当前用户交易所下的ETH
    const EtherExchange = await Exchange.methods.balanceof(ETHER_ADDRESS,account).call()
    console.log(EtherExchange)
    
    • 1
    • 2
    • 3

    还是用我们之前交易所写的balanceof 方法 这次 我们的token地址换成了 ETH 账号还是当前账号
    一运行 也是零 没毛病啊 毕竟没有存过
    在这里插入图片描述
    最后 我们还是这样写入一下就好了
    在这里插入图片描述

  • 相关阅读:
    docker--在Anaconda jupyter 容器中使用oracle数据源时,Oracle客户端安装配置及使用示例
    【cmake实战四】构建静态库和动态库——windows系统
    一文详解不同路径
    【译】了解17.10 GA 中最新的 Git 工具特性
    3.【刷爆LeetCode】找出字符串中第一个只出现一次的字符(多方法、多思路)
    java 把a.txt文件中的内容复制到当前项目目录下的b.txt文件中,2种方式比较复制效率 毫秒比较
    【Vue3-Flask-BS架构Web应用】实践笔记1-使用一个bat脚本自动化完整部署环境
    spring-boot-maven-plugin插件 —— 重新打包分类
    基于 Bresenham 算法画圆
    Go语言中获取IP
  • 原文地址:https://blog.csdn.net/weixin_45966674/article/details/134161849