【教程|翻译】EOS Todo Dapp——免费RAM模型和state管理

in #eos6 years ago

版权声明:

以下内容来自微信公共帐号“EOS技术爱好者”,搜索“EOSTechLover”即可订阅,翻译Gavin,校对Lochaiching。转载必须保留以上声明。仅授权原文转载。

本文原文链接为https://steemit.com/eos/@leordev/eos-todo-dapp-free-ram-model-and-state-management-eos-redux-3 ,由本号“EOS技术爱好者”翻译。

"EOS技术爱好者"全程由EOShenzhen运营,喜欢我们请为我们投票(EOShenzhen的投票账号:eoshenzhenio)!


EOS Todo Dapp - Free RAM Model and State Management - EOS + Redux = <3

WechatIMG231.jpeg

You probably heard about the benefits of working with immutable states. That's the whole base of the blockchain, in ugly words, a big immutable database (decentralized of course). I'm not entering in details of the advantages of immutability, it's already changing the world and you can see it in our lovely EOS blockchain.

你可能听说过保持不可变的状态来进行工作的好处,那是整个区块链的基础,通俗来说,就是一个大的、不变的数据库(当然它是去中心化的)。我不需要详细地来说明不可变性的优点,它已经改变了世界,并且你可以在我们生机勃勃的EOS区块链中看到它。

Now, inspired by the amazing DecentTwitter FREE RAM Dapp built by @kesarito and also by our outrageous EOS RAM price, I was studying and playing with Free RAM models and what should go inside EOS multi_index tables and what does not.

我的灵感来自于@kesarito打造的令人惊叹的DecentTwitter FREE RAM Dapp,以及我们夸张的EOS RAM价格。我正在学习和使用免费的RAM模型,以及考虑应该在EOS多索引表格中使用什么,和不使用什么。

Functional Programming FTW

函数式编程增值

Working a lot in the last years with functional languages like Elixir and Elm, you have something in common: Immutability. The state is immutable and you actually use functions to update the state. You are not really updating a state, you are always creating a new one as it's immutable.

在过去的几年里,你使用了诸如Elixir和Elm之类的函数式语言,你会发现它们有一些共同之处:不可变性。状态是不可变的,实际上你是使用函数来更新状态。你并不是在更新一个状态,而是在创建一个新的状态,因为它始终是不可变的。

I would like to pick The Elm Architecture in this example, as it is the perfect fit for browsers (which I think is the best use case in how to handle a million things running in parallel - and that's what EOS will achieve soon). So this is the Elm Model-View-Update Architecture:

我想在这个例子中选择 Elm 架构,因为它对于浏览器是最佳选择(我认为它是阐释如何并行处理百万级交易的最佳用例——这就是EOS即将实现的目标)。 Elm模型 - 视图 - 更新架构如下所示:
WechatIMG232.jpeg
Does it not look like theEOS Blockchain?

它看起来难道不像EOS区块链吗?
WechatIMG233.jpeg
My dirty analogy of The Elm Architecture to EOS Blockchain Action/State update flow

Explaining my flow:

Actions: an user (or oracle) sign and push an action

Update State: this is our smart contract validating and updating the tables (OPTIONAL!)

Transaction: if your smart contract ran fine a new transaction is generated and added to the next block

Everything above on top of EOS Blockchain which is generating blocks (with or without new transactions)

Yeah ok, maybe I'm just forcing something here but you will understand what I want to get if you follow me along.

我将Elm架构简单地类推到EOS区块链 Action/State更新流程

下面介绍我的流程:

1、Actions:用户(或者oracle)签名和推出一个action;

2、更新State:验证智能合约和更新表格(可选项!);

3、交易:如果你的智能合约运行正常,则会生成新交易并将其添加到下一个区块;

4、上面的一切都是在正在出块的EOS区块链之上的(不管有没有新的交易)。

好吧,也许我在这里强加给你很多事情,但是如果你跟着我继续了解下去,你就会明白我想要表达的是什么。

Optional RAM usage and introducing Redux to handle Data

可选的RAM使用和引入Redux来处理数据

In the above EOS Blockchain actions flow step 2 I said that update table is optional. And indeed it is, check DecentTwitter contract actions now:

在上述EOS区块链行为流程步骤2中,我说更新表是可选的。 事实确实如此,现在检查DecentTwitter合约action:

      void tweet(std::string msg) {}                                 

      void reply(std::string id, std::string msg) {}                 

      void avatar(std::string msg) {}         

Fantastic! It just has empty body actions. It does not update any contract table, actually this contract has no table at all. You know what it means? There's no RAM cost. Free RAM Dapp!

太棒了! 它只有空白的action。 它不会更新任何合同表,实际上这个合同根本没有表格。 你知道这意味着什么吗? 没有RAM成本!也就是这是免费RAM 的分布式应用!

The question here is: how to extract/generate state and handle the action data for more complex cases like an edit action? That's where I came up with Redux idea to handle the state. Redux was heavily inspired on Elm Architecture and that's why I introduced the whole Elm story above.

这里的问题是:如何提取/生成state并处理更复杂的情况(如编辑操作)的action数据? 这就是为什么我提出Redux想法来处理state。 Redux在Elm 结构上受到了很大的启发,这也是我在上面介绍整个Elm故事的原因。

So what we want to do to have a safe and well-managed immutable state application from the blockchain is:

Listen to EOS blockchain actions relevant to our dapp, usually our contract actions only

Every time this new action arrives we dispatch a Redux action

Redux will handle the state update based on the action

I think nothing like a real example to illustrate the case. Let's create a FREE RAM TO-DO List Dapp! (cliche...)

因此,我们想要做的是,从区块链中获得一个安全的、管理良好的不可变state的应用程序:

1、看看与我们的dapp相关的EOS区块链action,通常只有我们的合约action;

2、每次有新的action,我们都会调用一个Redux action;

3、ReDux将根据action来进行state的更新

我认为没有什么能像一个真实的例子来说明这个案例。让我们创建一个免费的RAM任务列表Dapp吧!(陈词滥调…)

EOS To-Do List Dapp

EOS任务列表分布式应用

This Dapp should allow us to handle our tasks in a to-do list fashion!

这个Dapp应该能让我们以待办事项列表的方式来处理我们的任务!

EOS Smart Contract

EOS智能合约

This is atwenty-ishlines contract, super simple:

这是一个20行左右的合约,超级简单:

#include <eosiolib/eosio.hpp>



using namespace eosio;

using std::string;



class todo : public eosio::contract {

  public:

      todo(account_name self)

      :eosio::contract(self)

      {}



      void addtodo(uint64_t id, string text) {}

      void edittodo(uint64_t id, string text) {}

      void toggletodo(uint64_t id) {}



};



EOSIO_ABI( todo, (addtodo)(edittodo)(toggletodo) )

The contract has three simple empty body actions:

addtodo - allows you to create a new task on your todo list

edittodo - allows you to edit a task description on your todo list

toggletodo - allows you to mark a task as completed or unmark the completed flag

这个合约有三个简单的、空白的action:

添加任务-允许你来创建一个新任务在你的任务列表

编辑任务-允许你来编辑一个任务内容在你的任务列表

切换任务-允许你来标记一个任务为已完成或者取消已完成的标记

Node.js Backend EOS Blockchain Listener with Redux store management

Node.js后端EOS区块链监听器和Redux存储管理

Here I will explain how Redux will work and how I came up with this integration. If you can't understand it at all, please check https://redux.js.org/ - the Introduction and Basics chapter, real quick!

这里我将解释Redux是如何工作的以及我是如何实现这个集成的。如果你根本无法理解,请查看https://redux.js.org/-介绍和基础章节,快速学习!

First step: setup the actions we want to listen from EOS Blockchain, in my case I deployed the above todo contract in an account called todo in my single local node:

第一步:设置我们监听EOS区块链的action,在我的例子中,在我的单个本地节点的一个名为todo的账户中部署了上述任务合约:

// actions mapping

const ACTION_ADD_TODO = 'addtodo'

const ACTION_EDIT_TODO = 'edittodo'

const ACTION_TOGGLE_TODO = 'toggletodo'



const CONTRACT_ACTIONS = [

  {

    account: 'todo',

    actions: [ACTION_ADD_TODO, ACTION_EDIT_TODO, ACTION_TOGGLE_TODO]

  }

]           

Second step: setup our initial state, how we want our store to look like. In our case it's just a simple todo list:

第二步:设置我们的初始state,以及希望的存储是什么样的。在例子中,这只是一个简单的待办事项列表:

// state

const initialState = {

  todos: []

}

From the above state we will listen the EOS Blockchain actions to decide how to store and manage the data.

从上面的state,我们将观察到EOS区块链action,来决定如何存储和管理数据。

Third step: setup the application reducer. Reducer (what originates the Redux name) is just a function that takes state and action as arguments, and returns the next state of the app. (Note the next state here, it's very important. Remember we are immutable, we don't change anything.)

第三步:设置应用程序Reducer。Reducer.(源自Redux的名称)只是一个将state和action作为参数的函数,并返回应用程序的下一个state(注意下一个state,它非常重要。记住,我们是不可变的,我们不会改变其中的任何东西。)

This is the application reducer code:

这里是应用程序Reeducer的源代码:

// reducer

const appReducer = (state = initialState, action) => {

  switch (action.type) {

    case ACTION_ADD_TODO:

      return addTodoReducer(state, action)

    case ACTION_EDIT_TODO:

      return editTodoReducer(state, action)

    case ACTION_TOGGLE_TODO:

      return toggleTodoReducer(state, action)

    default:

      // return the current state

      // if the action is unknown

      return state

  }

}

So in the above code we check the action type (action name from EOS) to decide which reducer we will call to update our state, if it's an unknown/not-mapped action it just ignores and returns the same state.

所以在上面的代码中我们检查action类型(来自EOS的action名称)来决定我们将调用哪个Reducer来更新我们的state,如果它是一个未知/未映射的action,它只是忽略并返回相同的state。

Add Todo reducer function - it checks if the id was never used and add to our todo list. Each todo has the fields id, text, author and completed:

添加任务Reducer函数 - 它会检查id是否从未使用过并添加到我们的待办事项列表中。 每个任务都有字段id,文本内容,作者和是否完成标记:

const addTodoReducer = (state, action) => {

  

  // check and do not add new todo if this todo id

  // already exists

  if (state.todos.filter(todo => todo.id === action.data.id).length > 0)

    return state

  

  const newTodo = {

    id: action.data.id,

    text: action.data.text,

    author: action.authorization[0].actor,

    completed: false

  }



  const newTodos = [ ...state.todos, newTodo ]



  return { ...state, todos: newTodos }

}

It's important to note above that we NEVER change the current state, we always create and return a new one. We do that because we are, again, immutable. It also allows us to have cool features like time-traveler debug, undo actions etc.

需要特别注意的是,我们从不改变当前state,我们总是创建并返回一个新的state。这样做是因为我们是不可变的。这就允许我们有一些很酷的特性,比如实时的调试、撤销action等等。

Edit todo reducer - here we simply update the text of a todo, ONLY IF the actor for this action is the same as the one that created this todo task:

编辑任务Reducer-在这里我们只是简单地更新任务的文本,只有这个action的作者与任务的行为人相同时才会触发:

const editTodoReducer = (state, action) => {

  const updatedTodos = state.todos.map(todo => {

    if (todo.id === action.data.id &&

      todo.author === action.authorization[0].actor) {

      return {

        ...todo,

        text: action.data.text  // update text

      }

    } else {

      return todo

    }

  })



  return { ...state, todos: updatedTodos }

}

Toggle todo reducer - same as edit todo, it verifies if the actor is the same as the author of the task, this time it just toggles the completed boolean field:

切换任务Reducer-类似于编辑任务,它验证行为人是否与任务的作者相同,这一次它只是用来切换表示是否完成的布尔字段:

const toggleTodoReducer = (state, action) => {

  const updatedTodos = state.todos.map(todo => {

    if (todo.id === action.data.id &&

      todo.author === action.authorization[0].actor) {

      return {

        ...todo,

        completed: !todo.completed  // toggle boolean

      }

    } else {

      return todo

    }

  })



  return { ...state, todos: updatedTodos }

}

Now the only thing left is to initialize the store! Here's the simple code:

现在剩下唯一的一件事就是来初始化存储状态,这里是简单的源代码:

// initialize redux store

const store = createStore(appReducer)



// Log the initial state

console.log('>>> initial state: \n', store.getState(), '\n\n')



// Every time the state changes, log it

store.subscribe(() =>

  console.log('>>> updated state: \n', store.getState(), '\n\n')

)

Actually only the first couple lines of the code is relevant. I'm just logging the initial state which prints this:

实际上,代码的前几行是相关的。我只是记录并打印初始state:

>>> initial state:

 { todos: [] }

And subscribing the state which prints the full state each time that we update the state. The cool thing about Redux subscription is that you could use it to serve websockets to dapp users or write to your database, and so on. The possibilities are endless.

并且在每次更新state的时候,订阅并打印完整state信息。关于Redux订阅的一个很酷的事情是你可以用它来为dapp的用户提供websockets或者写入你的数据库等等。 可能性是无限的。

Finally the last step is to dispatch the Redux actions every time that we have a relevant action. The blockchain listener that I built is not relevant, because you probably have yours already done, but what I'm basically doing is listening to each new block in the chain, checking if it has any transaction inside this block, selecting all the actions of it and finally filtering and dispatching each one to our reducer:

最后一步是每次我们有相关action时,调用Redux action。我建立的区块链的监听器并不是相关的,因为你可能已经完成这个工作,但我主要做的是监听链中的每一个新块,检查是否有任何交易在这个块中,选择所有的action然后过滤,并且将每一个都分配给我们的Reducer:

const filterAndDispatchAction = (newAction, trxId, block) => {

  const action = {

    type: newAction.name,

    account: newAction.account,

    authorization: newAction.authorization,

    data: newAction.data

  }



  const subscribed = CONTRACT_ACTIONS.find(item => (

    item.account === action.account &&

      item.actions.indexOf(action.type) >= 0

  ))



  if (subscribed) {

    console.log(`\nDispatching Action from Block ${block} - Trx ${trxId}:\n`,

      action, '\n\n')

    store.dispatch(action)

  }



}

Remember the initial CONTRACT_ACTIONS we created in the beginning of this? Yes, it's finally being used to filter the relevant actions that you want.

还记得将我们一开始创建的CONTRACT_ACTIONS初始化吗?对,它最终用于过滤你想执行的相关action。

Also the store variable that contains the Redux state handler, is being used to dispatch the action received from the blockchain to our Redux store. This is how everything connects. If everything goes right you will see the following log in the console:

此外,包含Redux state处理程序的存储变量被用于将区块链接收的action,分派给我们的Redux存储。 这就解释了一切是如何连接。 如果一切顺利,您将在控制台中看到以下日志:

Dispatching Action from Block 81 - Trx a57a690978b78b18a3a5b2869d7734eb3da127147f256ff0ff74022f9adabd08:

 { type: 'addtodo',

  account: 'todo',

  authorization: [ { actor: 'eosio', permission: 'active' } ],

  data: { id: 1, text: 'I need to add a database' } }





>>> updated state:

 { todos:

   [ { id: 1,

       text: 'I need to add a database',

       author: 'eosio',

       completed: false } ] }

Playing Todo Dapp using our backend Node.js Redux store application

使用我们的后端Dapp.js Redux存储应用程序播放Todo

The whole code is in this repository: https://github.com/leordev/eos-redux-todo

完整源代码保存在这个地址中:https://github.com/leordev/eos-redux-todo

You can use the blockchain listener idea, it's super simple there and probably needs some polishing. Also I should refactor this code into different files, I just kept it simple with everything inside index.js

你可以使用区块链监听器的想法,它真的非常简单,可能需要一些修改。另外,我应该将这些代码重构为不同的文件,我只是简单地将其全部包含在了index.js中。

Instructions to Deploy the Contract

部署合约的指令

cleos create account eosio todo OWNERPUBKEY ACTIVEPUBKEY

cd eos-redux-todo/todo

eosiocpp -o todo.wast todo.cpp

eosiocpp -g todo.abi todo.cpp

cleos set contract todo ../todo

Instructions to Init the Nodejs Redux Store Application

初始化Nodejs Redux存储应用程序的指令

cd eos-redux-todo

npm install

node index.js 

Demoing

演示

eos-redux-demo.gif

Wrapping it Up

包装起来

That's it folks, that's the best way that I found to handle store management from EOS Blockchain Actions, using Redux which is a very popular and solid tool created by Dan Abramov from Facebook team. This is a nice library and has a lot of functionalities out-of-the-box like the subscription, it's easy to implement undo and time-traveling states. It's very mature and you have nice tooling around it like redux-logger and redux-devtools.

就是这些了,朋友们,这是我觉得解决来自EOS Blockchain Actions存储管理的最佳方式,Redux是由Facebook团队的Dan Abramov创建的非常流行且可靠的工具。 这是一个很好的库,并且具有许多开箱即用的功能,如订阅,它很容易实现撤消和实时state。 你可以使用非常成熟的redux-logger和redux-devtools等等这些很好的工具。

The 100% free RAM model is an interesting approach but I think it has some flaws. E.g. the way I'm filtering the editing/toggling in the todo record by author, I think it should be blocked in the EOS chain not allowing the transaction to be created, but actually we don't have a table inside the contract to match the todo ids with the respective authors. So if we have other apps consuming this contract it could wrongly consider that the todo was edited by a bad actor if it does not take care of this very same filter rule that we did in our application.

完全免费RAM模型是一个有趣的方法,但我认为它有一些缺陷。 例如。 我通过作者过滤任务记录中的编辑/切换操作,认为这在EOS链中应该被阻止并且不允许创建交易,但实际上我们在合同中没有一个表格可以用来匹配任务id与各自的作者。因此,如果我们有其他应用程序使用这个合约,如果不使用我们在应用程序中所使用的相同的过滤规则,它可能会错误地认为该任务是由一个非法用户编辑的。

I would love to hear your feedbacks in how you are managing this and your thoughts in this approach. After we have solid progress in this Redux integration and some use cases with standardized actions I could wrap it up in a JS library and integrate with eosjs. That would be nice, just not sure if it makes sense yet. See you all!

我很想听听你对此方法的反馈以及你对此方法的看法。在我们在Redux集成和一些使用标准化操作的用例中取得了稳定的进展之后,我可以将它封装在JS库中,并与eosjs集成。能做到的话将会很好,只是不确定它是否有意义。下次见!

本文图片来源于英文原文


了解更多关于EOShenzhen:

We are EOShenzhen

不同入口如何投票:
imToken
火币
portal

关于我们更多联系:
Website:https://eoshenzhen.io

Steem:https://steemit.com/@eoshenzhen

Busy:https://busy.org/@eoshenzhen

Telegram:https://t.me/eoshenzhen

Twitter:https://twitter.com/eostechlover

简书:EOS技术爱好者

新浪微博:EOSTechLover


EOShenzhen的投票账号:eoshenzhenio
100x100-IMG_7963_small.png

Coin Marketplace

STEEM 0.31
TRX 0.11
JST 0.033
BTC 64733.60
ETH 3170.85
USDT 1.00
SBD 4.16