Unity - UIWidgets 5. Redux接入(一) 基本接入
对响应式的数据组织结构不太熟, 直接尝试Redux走起
参考资料
Redux的基本概念
state 一个字段用于存储状态
存储state的地方称为"store", 类似Model\DataCenter等, 把所有数据(state)存进去(类似playerprefs), 不对外提供setter方法
实际类似一个全局存储字典, 但难点在于如何组织数据, 比如玩家背包里面N种道具要想办法合理的存进去, 还要方便维护
action 修改数据的指令
要修改state只能通过action. 这样的话通过跟踪action的日志可以很清晰的知道到底做了什么
reducer
把action和reducer连接起来, 实际相当于一个switch-case函数, 在复杂项目中把action划分为多个模块
和常见的MVC模式的对比
普通的MVC模式为每个不同的模块创建一个Model, 通过Controller管理, 优点是对象式存储比较符合直觉
Redux把所有数据存储到唯一一个state中, 通过action来发送指令, 优点是容易追踪操作
Redux的三个原则
- 单一数据源 - 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
- State 是只读的 - 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
- 使用纯函数来执行修改 - 为了描述 action 如何改变 state tree ,你需要编写 reducers。
前两点上面已经说过, 主要说一下第三点
Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器。
这里它接收先前的 state 和 action,并返回新的 state
, 第一个state指现在的状态参数, 第二个state指返回值
纯函数:
- 对于同一参数,返回同一结果
- 结果完全取决于传入的参数
- 不产生副作用
大概说来Reducer就是
public static Object SomeReducer(Object state, Action action)
{
// 对于static变量只使用get方法
switch (action.actionType)
{
case "SOME_ACTION":
//
state.foo = "bar";
// ... 通过某种方式划分到其他的reducer
}
return ...;
}
即假设小明现在14岁(state), 过完生日要加一岁(action), 之后获取最后的年龄结果(state), 伪代码类似这样SomeFunction(14, ["ADD_AGE", 1]) -> 15
redux的官方文档很好理解
function visibilityFilter(state = 'SHOW_ALL', action) {
//...
}
function todos(state = [], action) {
//...
}
import { combineReducers, createStore } from 'redux'
let reducer = combineReducers({ visibilityFilter, todos })
let store = createStore(reducer)
Redux的基本概念快速看了一遍, 但真正用起来怎么样, 又是另一回事了, 特别返回state这里, 还是很迷惑的
Redux接入
熟悉Redux的概念, 接下来就是接入Redux接口. 首先考虑一个简单的应用:
界面显示一个数, 有两个按钮, 一个把数字加一, 一个把数字减一, 类似UIWidgets给的示例
按照之前statefulwidget的写法, 是这样
using System.Collections.Generic;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace UI
{
public class HomeRoute : StatefulWidget
{
public override State createState()
{
return new HomeRouteState();
}
}
class HomeRouteState : State<HomeRoute>
{
private int m_Count = 0;
public override Widget build(BuildContext context)
{
Scaffold scaffold = new Scaffold(
appBar: new AppBar(
title: new Text("首页")
),
body: new Center(
child: new Column(
children: new List<Widget>
{
new Text(
data: m_Count.ToString(),
style: new TextStyle(
fontSize: 30
)
),
new Padding(padding: EdgeInsets.symmetric(10)),
new RaisedButton(
child: new Icon(Icons.add),
onPressed: () =>
{
setState(() =>
{
m_Count++;
});
}
),
new Padding(padding: EdgeInsets.symmetric(10)),
new RaisedButton(
child: new Icon(Icons.remove),
onPressed: () =>
{
setState(() =>
{
m_Count--;
});
}
),
}
)
)
);
return scaffold;
}
}
}
如果使用redux后, 把存储在控件中的m_Count移到全局的store中, 修改操作也通过action执行.
但首先需要接入redux
参考UIWidgets提供的示例"CounterAppSample.cs"
GlobalState.cs
namespace UI
{
public class GlobalState
{
public int Count { get; private set; }
public GlobalState(int count = 0)
{
Count = count;
}
}
}
Actions.cs
namespace UI
{
public class AddAction
{
public int amount;
}
public class RemoveAction
{
public int amount;
}
}
HomeRoute.cs
using System.Collections.Generic;
using Unity.UIWidgets;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.Redux;
using Unity.UIWidgets.widgets;
namespace UI
{
public class HomeRoute : StatelessWidget
{
public override Widget build(BuildContext context)
{
Store<GlobalState> store = new Store<GlobalState>(
reducer: Reducer,
initialState: new GlobalState()
);
return new StoreProvider<GlobalState>(store, GetWidget());
}
//因为Reducer签名问题, 参数为object, 可以修改源码"xxxuiwidgets\Runtime\redux\store.cs"自定义为IAction接口, 不推荐
public static GlobalState Reducer(GlobalState state, object action)
{
if (action is AddAction)
{
return new GlobalState(state.Count + ((AddAction)action).amount);
}
else if (action is RemoveAction)
{
return new GlobalState(state.Count - ((RemoveAction)action).amount);
}
else
{
// action不匹配时返回原state
return state;
}
}
private Widget GetWidget()
{
Scaffold scaffold = new Scaffold(
appBar: new AppBar(
title: new Text("首页")
),
body: new Center(
child: new Column(
children: new List<Widget>
{
new StoreConnector<GlobalState, string>(
converter: (state) => state.Count.ToString(),
builder: (context, text, dispatcher) => new Text(
data: text,
style: new TextStyle(
fontSize: 30
)
),
pure: true // 这个参数不知道干嘛用的
),
new Padding(padding: EdgeInsets.symmetric(10)),
new StoreConnector<GlobalState, object>(
converter: (state) => null,
builder: (context, _, dispatcher) => new RaisedButton(
child: new Icon(Icons.add),
onPressed: () =>
{
dispatcher.dispatch(new AddAction { amount = 1 });
}
),
pure: true
),
new Padding(padding: EdgeInsets.symmetric(10)),
new StoreConnector<GlobalState, object>(
converter: (state) => null,
builder: (context, _, dispatcher) => new RaisedButton(
child: new Icon(Icons.remove),
onPressed: () =>
{
dispatcher.dispatch(new RemoveAction { amount = 1 });
}
),
pure: true
),
}
)
)
);
return scaffold;
}
}
}
到现在产生的疑问
上面的代码运行起来没问题, 但在之前没完全理解的地方, 疑问出现了. 把state类起名为"GlobalState", 说明我是把这个看做存储所有全局状态的, 但参照reducer, 每次返回一个新的GlobalState类, 如果每次复制更新造成很大浪费, 似乎不太对
redux的文档中提到
注意 todos 依旧接收 state,但它变成了一个数组!现在 todoApp 只把需要更新的一部分 state 传给 todos 函数,todos 函数自己确定如何更新这部分数据。这就是所谓的 reducer 合成,它是开发 Redux 应用最基础的模式。
即有的地方是传state的一个部分过去的
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: todos(state.todos, action) // 注意这里, 传递的是一个部分
})
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: todos(state.todos, action)
})
default:
return state
}
}
在js这么搞没问题, 强类型的C#\UIWidgets中要怎么实现?
Unity - UIWidgets 5. Redux接入(一) 基本接入的更多相关文章
- 【Unity游戏开发】接入UWA_GOT的iOS版SDK以后无法正常出包
一.正文 问: RT,最近有看到UWA_GOT工具新增了iOS版本的支持,于是下载了最新的工具包进行了接入测试.是按照文档直接将UWA_GOTv2.0.1_iOS.unitypackage导入进了Un ...
- Unity使用TUIO协议接入雷达
本篇文章不介绍Unity.TUIO.雷达是什么以及有什么作用.刚接触TUIO的亲们,建议直接硬刚.至于刚接触Unity的亲,这边建议亲直接放弃治疗呢 下面开始正儿八经的教程 需要准备的东西 Unity ...
- Unity 之 Redux 模式(第一篇)—— 人物移动
作者:软件猫 日期:2016年12月6日 转载请注明出处:http://www.cnblogs.com/softcat/p/6135195.html 在朋友的怂恿下,终于开始学 Unity 了,于是有 ...
- iOS Unity 项目解析
本文旨在记录Unity 导出的iOS 项目笔记,另带接入SDK的终极方案,顺带对比Android 项目 1蓝色的目录 Data 这个就是项目的数据,每个项目不一样也就是这个目录不一样,是不是可以把这个 ...
- 干掉Unity3D
我为什么想干掉Unity3D? 这个问题容我不答,每个做技术的人总有一些完美主义. 你使用u3d的过程中是不是痛并快乐着呢. 就从两个国内具有相当普遍性的痛点说起. il2cpp,unity作出了这个 ...
- Unity_UIWidgets - 组件Container
Unity_UIWidgets - 组件Container Container 构造 效果 结语 QQ 今日无推荐 Unity_UIWidgets - 组件Container 上周给大家讲完了Scaf ...
- Unity_UIWidgets - 组件AppBar
Unity_UIWidgets - 组件AppBar AppBar 构造 构造png观看 使用代码 使用效果 AppBar使用结束 结语 图标Icon QQ 今日无推荐 Unity_UIWidgets ...
- Unity_UIWidgets新手入门
Unity_UIWidgets新手入门 Hello Everyone!好久没见了,有没有有些想念小黑呢?什么?这么想?哈哈哈哈哈哈,不过我也知道你是想了解新的知识了,才不是想我嘞. 好了,好歹也半年没 ...
- Unity - 接入Android SDK
在网络上,关于Unity与Android如何进行交互,雨松MOMO大神已经有两篇文章简单介绍了如何操作(1)Unity3D研究院之打开Activity与调用JAVA代码传递参数(2)Unity3D研究 ...
- unity工程接入Android sdk后真机测试解锁屏后退出的解决
unity工程接入如91.移动支付等Android sdk后,真机运行尤其是在4.0+以上坏境,往往会出现解锁屏后退出的情况,解决办法如下: 可以在AndroidManifest.xml中所有的con ...
随机推荐
- HTML前端js
ajax请求方法书写 $.ajax({ type:"POST", url:CONTEXT_PATH+"/appAudit/insertSnDocCountAdmin&qu ...
- Python中的弱引用与基础类型支持情况探究
背景 最近有一个业务场景需要用Python自行实现一个简单的LRU cache,不可避免的接触到了弱引用这一概念,这里记录一下. 强引用 Python内存回收由垃圾回收器自动管理,当一个对象的引用计数 ...
- .NET 如何实现ChatGPT的Stream传输
.NET 如何实现ChatGPT的Stream传输 ChatGPT是如何实现不适用websocket进行一个一个字返回到前端的? 下面我们会介绍一下EventSource EventSource Ev ...
- 【go语言】2.3.1 错误处理的基本概念
在 Go 语言中,错误处理是通过返回错误值进行的,而不是像一些其他语言那样通过抛出和捕获异常.Go 语言有一个内置的接口类型 error,专门用于处理错误. error 接口 error 是一个内置的 ...
- 监控keepalived_vip控制容器的状态
需求:监控server服务器的vip状态,如果vip存在,则判断容器是否启动,如果没有启动,则启动容器.如果vip不存在则关闭容器. 方法一: 查看代码 #!/bin/bash ip add | gr ...
- 开机自动打开termux以及启动termux的服务
ps:因为我们的服务是安装在平板上面的termux,客户不想维护麻烦,如果平板重启之后还需要手动启动ternux,还要开启命令启动服务,这样比较麻烦,所以研究如下操作 1.安装macroDroid 直 ...
- .NET周刊【8月第2期 2023-08-14】
本周由于Myuki大佬感染新冠,国际板块暂停更新一周,将在下周补齐,所以本周只有国内板块. 国内文章 解决 Blazor 中因标签换行导致的行内元素空隙问题 https://www.cnblogs.c ...
- MQTT vs. XMPP,哪一个才是IoT通讯协议的正解
MQTT vs. XMPP,哪一个才是IoT通讯协议的正解 这是个有趣的话题! 先来聊几个小故事. 关于我和MQTT 我在人生第一个IoT项目里,第一次接触到MQTT协议. 我很快就理解了这个协议.因 ...
- JSTL fn函数使用总结
首先,我们要在页面的最上方引用: <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/function ...
- 五分钟 k8s入门到实战--跨服务调用
背景 在做传统业务开发的时候,当我们的服务提供方有多个实例时,往往我们需要将对方的服务列表保存在本地,然后采用一定的算法进行调用:当服务提供方的列表变化时还得及时通知调用方. student: url ...