上文 web3 在React dapp中全局管理web3当前登录用户/智能合约等信息中 我们简单操作,将web3的公共信息都存在了window对象上
然后 我们先来启动一下环境
终端输入
ganache -d
打开项目 终端输入
truffle migrate --reset
在区块链上发布一下智能合约
然后 我们在 src下的 components 目录下创建两个组件
分别是 余额组件 balance.jsx 订单组件 Order.jsx
然后 在我们 src下的view下的index.jsx组件中引入他们
我们这里 直接导入 然后使用他们
那么 我们余额的管理 显然是不能用组件传值的 因为他要动态的去变动
redux就是一个非常不错的选择
那么 我们终端执行
npm i --save redux react-redux
引入一下依赖
然后 我们在src目录下创建文件夹 redux 下面创建一个 store.js
然后 我们还需要一个依赖
终端输入
npm i --save @reduxjs/toolkit
然后 我们在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;
这里 我们存储了当前登录用户的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;
这里 我们导入 自己刚才写的 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>
);
}
其实关键就在于 通过react-redux 导入Provider组件 嵌套我们的路由组件 让所有的路由组件都能享受到共享的数据资源
然后这个数据资源来自 我们配置的redux/store
然后 src目录下的 components目录下 Order.jsx
这里 我们保证这个组件格式不要有问题就好
import React from 'react';
export default function Order() {
return (
<div>
订单组件
</div>
);
}
然后 我们在 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>
);
}
这里 我们拿取了 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>
);
}
然后 我们要做的是 在项目初始化时 就拿到用户的这些信息
那么 在哪里拿?
我们打开 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)
}
)
逻辑的话 我们先写的比较简单 就把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>
);
}
做了部分语法改动 但其实说到底 唯一的改变就是 在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信息
}
)
这里 我们通过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)
通过Exchange交易所 合约 调用他的balanceof函数 这里 我们通过grToken合约对象 拿到他的地址 然后传入当前账号的地址
最后得到token信息
然后在控制台输出
运行结果如下
啊 0 没毛病啊
我们并没有往交易所中存储grtoken 所以是没什么问题的
好 那 这里 我们获取到了 就赋值回调一下
然后 获取用户当前的ETH 这个就比较简单了 我们很早就演示过
直接编写代码如下
const EtherWallet = await web3.eth.getBalance(account)
console.log(EtherWallet)
直接用web3 对象的getBalance 然后传入当前账号就可以了
运行代码 查看看着他打印
没有任何问题 还是一个最小单位 没有转换过的
然后 获取到之后 我们还是一样做个数据的写入
最后一个 获取在交易所中的 ETH
ETH的地址 我们之前写脚本的时候用过
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';
我们直接将这个拿过来
写到我们 src下的 redux下的balanceSlice下的 index中
然后 我们最下面直接这样写
//获取当前用户交易所下的ETH
const EtherExchange = await Exchange.methods.balanceof(ETHER_ADDRESS,account).call()
console.log(EtherExchange)
还是用我们之前交易所写的balanceof 方法 这次 我们的token地址换成了 ETH 账号还是当前账号
一运行 也是零 没毛病啊 毕竟没有存过
最后 我们还是这样写入一下就好了