Peel the comparison to the original see Code 17: How does the original show the details of the transaction?

Source: Internet
Author: User

Author: freewind

Compared to the original project warehouse:

GitHub Address: Https://github.com/Bytom/bytom

Gitee Address: Https://gitee.com/BytomBlockc ...

In the last article, we have one small problem left unresolved, that is, how the front end shows the details of a transaction.

First look at the corresponding picture:


This picture is too long, divided into two, can actually be regarded as one.

So how does this page come from? After displaying the transaction summary information in the previous list, you can click the "View Details" link in the upper right corner of the summary information to open it.

So let's look at the details of how this transaction is displayed in this article.

Since it is divided into front and rear ends, we will divide it into two small problems as before:

    1. How the frontend sends the request to the background and displays the data
    2. Back end is how to get the corresponding data sent to the foreground

It is necessary to note that this table contains a lot of information, but we do not intend to explain it in this article. Because you can understand a look can be understood, do not understand the need for accurate understanding of the core of the original before we can explain clearly, and this one until we later specialized research.

How the frontend sends the request to the background and displays the data

Let's start by looking at the route path that shows the transaction detail page. When we put the mouse on the "View Details" in the upper right corner of the trading summary page, we find that the URL looks like this:

http://localhost:9888/dashboard/transactions/2d94709749dc59f69cad4d6aea666586d9f7e86b96c9ee81d06f66d4afb5d6dd

Which http://localhost:9888/dashboard/ can be seen as the root path of the application, then the route path should be /transactions/2d94709749dc59f69cad4d6aea666586d9f7e86b96c9ee81d06f66d4afb5d6dd , the subsequent length is obviously an ID, so we should look in the code similar to /transactions/:id such a string, oh, unfortunately did not find ...

That can only start from scratch, first find the definition of the front-end route:

Src/routes.js#l15-l35

// ...import { routes as transactions } from 'features/transactions'// ...const makeRoutes = (store) => ({  path: '/',  component: Container,  childRoutes: [    // ...    transactions(store),    // ...  ]})

Which transactions is what we need, and it corresponds to features/transactions/routes.js :

Src/features/transactions/routes.js#l1-l21

import { List, New, AssetShow, AssetUpdate } from './components'import { makeRoutes } from 'features/shared'export default (store) => {  return makeRoutes(    store,    'transaction',    List,    New,    Show,    // ...  )}

This function will transactions generate a lot of related routing paths. When we pass in some components, such as list display List , new New , show details and Show so on, we makeRoutes will add the associated path according to the predefined path rules. Let's take a look at makeRoutes :

Src/features/shared/routes.js#l1-l44

 import {Routingcontainer} from ' features/shared/components ' import {humanize} from ' utility/ String ' Import actions from ' actions ' const Makeroutes = (store, type, List, New, Show, options = {}) = = {Const LOADPAG      E = () = {Store.dispatch (Actions[type].fetchall ())} const Childroutes = [] if (New) {Childroutes.push ({   Path: ' Create ', component:new})} if (options.childroutes) {Childroutes.push (... options.childroutes)}   1.        if (Show) {Childroutes.push ({path: ': Id ', component:show})} return {//2. Path:options.path | | Type + ' s ', Component:routingcontainer, Name:options.name | | humanize (type + ' s '), Name_zh:options.name_zh, Indexroute: {component:list, OnEnter: (Nextstate, Replac E) = = {LoadPage (nextstate, replace)}, OnChange: (_, nextstate, replace) = {LoadPage (nextstate, Replace)}}, Childroutes:childroutes}}  

This code looks familiar because we've seen it before when we looked at the list of balances and trades. And what we're concerned about today is the Show code labeled 1th Place.

As you can see, when you pass in a Show component, you need to generate the associated route path for it. Specifically, childRouters add a path to :id , and its own route path is defined in the 2nd place, the default is, type + 's' and for this example, type the value is transaction , so the Show corresponding full path is /transactions/:id , That's what we need.

Back to the 1th code, you can see that the Show component is coming in from the outside, and from the previous function you can see that it corresponds to src/features/transactions/components/Show.jsx .

Let's go inside and look at this Show.jsx , first, the function that defines the HTML component render :

Src/features/transactions/components/show.jsx#l16-l96

class Show extends BaseShow {  render() {    // 1.    const item = this.props.item    const lang = this.props.lang    const btmAmountUnit = this.props.btmAmountUnit    let view    if (item) {      // ..      view = <div>        <PageTitle title={title} />        <PageContent>          // ...          <KeyValueTable            title={lang === 'zh' ? '详情' : 'Details'}            items={[              // ...            ]}          />          {item.inputs.map((input, index) =>            <KeyValueTable              // ...            />          )}          {item.outputs.map((output, index) =>            <KeyValueTable              // ...            />          )}        </PageContent>      </div>    }    return this.renderIfFound(view)  }}

The code was much simplified by me, mostly omitting the computation of a lot of data and the parameters of some display components. I divided the code into 2 sections:

    1. The 1th place to note is similar to the const item = this.props.item code, here is the item data we want to show, corresponding to this article is an transaction object, it is this.props obtained from the, so we can infer in this file (or a referenced file), there will be a connect method, Plug the data in the store. Let's go and have a look. The next two lines are like that.
    2. The 2nd code is basically the definition of Page view, you can see that the main use of the other custom components KeyValueTable . Code we will not follow the previous page effect we can imagine it is the form of some key-value data display.

Then we continue to look for connect , soon after the same page, found the following definition:

src/features/transactions/components/show.jsx#l100-l117

import { actions } from 'features/transactions'import { connect } from 'react-redux'const mapStateToProps = (state, ownProps) => ({  item: state.transaction.items[ownProps.params.id],  lang: state.core.lang,  btmAmountUnit: state.core.btmAmountUnit,  highestBlock: state.core.coreData && state.core.coreData.highestBlock})// ...export default connect(  mapStateToProps,  // ...)(Show)

I only left what I needed to pay attention to mapStateToProps . As you can see, the assignments of several variables we see in the 1th place here are defined, most importantly item , from the current state of the store state transaction items .

So state.transaction what is it? I began to think that it is our back from the background of some data, using transaction the name in the store, the results are not searched, and finally found that the original is not.

The reality is that where we define reducer, there is one makeRootReducer :

Src/reducers.js#l1-l62

// ...import { reducers as transaction } from 'features/transactions'// ...const makeRootReducer = () => (state, action) => {  // ...  return combineReducers({    // ...    transaction,    // ...  })(state, action)}

Originally it was built here. First of all, { transaction } this ES6 grammar, the usual wording, is:

{  transaction: transaction}

In addition, combineReducers this method is used to merge multiple reducer (perhaps because the store is too big, so split it into multiple reducer management, each reducer only need to deal with the part of their interest), and after merging, the store will become like this:

{    "transaction": { ... },    // ...}

So the front state.transaction is referring to here { ... } .

So continue, in the previous code, you can state.transaction.items[ownProps.params.id] see, there is state.transaction a items property, it holds a back to the table to /list-transactions retrieve a transaction array, when it was added to it?

This problem baffled me, I spent a few hours searching over the original front and back of the warehouse, have not found, finally had to exert the chrome Redux Devtools Dafa, found in the beginning, there are items :

On the figure there are two red boxes, the left side of the expression I now choose is the initial state, the right side of the beginning of the show has transaction been items , so suddenly, this is not the same as the previous reason! So soon the definition was found:

Src/features/transactions/reducers.js#l7-l16

export default combineReducers({  items: reducers.itemsReducer(type),  queries: reducers.queriesReducer(type),  generated: (state = [], action) => {    if (action.type == 'GENERATED_TX_HEX') {      return [action.generated, ...state].slice(0, maxGeneratedHistory)    }    return state  },})

Sure enough, it's also a combineReducers combination of several reducer, so the store will have several keys here, including, and what items we don't care about queries generated .

It took the whole afternoon to figure out the piece. It seems that for the analysis of dynamic language, it is important to open the brain hole, can not be preset reasons, in addition to use a variety of debugging tools, from different angles to view the data. But for Redux's Chrome plugin, I don't know how long it will take.

I personally prefer the static type of language, for JavaScript this, unless million can hide to hide, the main reason is that the code references to each other too few clues, many times must look at the document, code and even to guess, can not take advantage of the editor to provide the jump function.

After knowing state.transaction.items the origin, the following things will say. We are from the transaction of the state.transaction.items[ownProps.params.id] current needs, then state.transaction.items when is the time to put in the data?

Let's go back to the front makeRoutes :

Src/features/shared/routes.js#l1-l44

// ...import actions from 'actions'const makeRoutes = (store, type, List, New, Show, options = {}) => {  // 2.  const loadPage = () => {    store.dispatch(actions[type].fetchAll())  }  // ...  return {    path: options.path || type + 's',    component: RoutingContainer,    name: options.name || humanize(type + 's'),    name_zh: options.name_zh,    indexRoute: {      component: List,      onEnter: (nextState, replace) => {        loadPage(nextState, replace)      },      // 1.       onChange: (_, nextState, replace) => { loadPage(nextState, replace) }    },    childRoutes: childRoutes  }}

In the 1th place above, for indexRoute , there is a onChange trigger. It means that when the path of the route has changed and the new path belongs to the path (or child path) of the current index route, subsequent functions will be triggered. The definition in the later function is loadPage in the 2nd code, and it actions[type].fetchAll() dispatch the resulting action. As type is the case in this article transaction , through step-by-step tracking (a little bit of trouble here, but we have gone through the previous article), we found that the actions[type].fetchAll corresponding src/features/shared/actions/list.js :

src/features/shared/actions/list.js#l4-l147

export default function(type, options = {}) {  const listPath  = options.listPath || `/${type}s`  const clientApi = () => options.clientApi ? options.clientApi() : chainClient()[`${type}s`]  // ...  const fetchAll = () => {    // ...  }  // ...  return {    // ...    fetchAll,    // ...  }}

If we are still impressed with this piece of code, we will know that it will eventually go to the backend and /list-transactions call after the data is received dispatch("RECEIVED_TRANSACTION_ITEMS") , and it will be processed by this reducer:

Src/features/shared/reducers.js#l6-l28

export const itemsReducer = (type, idFunc = defaultIdFunc) => (state = {}, action) => {  if (action.type == `RECEIVED_${type.toUpperCase()}_ITEMS`) {    // 1.    const newObjects = {}    // 2.    const data = type.toUpperCase() !== 'TRANSACTION' ? action.param.data : action.param.data.map(data => ({      ...data,      id: data.txId,      timestamp: data.blockTime,      blockId: data.blockHash,      position: data.blockIndex    }));    // 3.     (data || []).forEach(item => {      if (!item.id) { item.id = idFunc(item) }      newObjects[idFunc(item)] = item    })    return newObjects  }  // ...  return state}

Explain the three code in this function in turn:

    1. The 1th place is to create a new empty object newObjects , which will be replaced at the end state.transaction.items and will be assigned a value in it later
    2. The 2nd place is to do some processing of the incoming data, and if the type is transaction , it will raise some of the attributes in each element of the array to the root for ease of use
    3. The 3rd place is to put each element into the newObjects id key, the object itself is value

After these processing, we can use to state.transaction.items[ownProps.params.id] get the appropriate transaction object, and by the Show.jsx display.

This piece of the front is basically clear. We continue to look at the back end

Back end is how to get the corresponding data sent to the foreground

As we said earlier, based on previous experience, we can push the export front end to access /list-transactions this interface on the backend. We are delighted to find that this interface has been studied in the previous article and can be skipped completely here.

So far, we have finally figured out the basic process of "How to create a deal". While there are a lot of details, and the knowledge that touches the core is ignored, it feels like there is more to it than the internal workings of the original.

Perhaps the knowledge accumulated now is about the same as the core of the original. In the next article, I will try to understand and analyze the core of the original, and in the course of learning, it may take a different approach to the current exploration process decomposition problem. In addition, it may take a lot of time, so the next article will come later. Of course, if the failure, indicating that my current accumulated knowledge is not enough, I also need to go back to the current practice, find ways to peel more than the original shell from different places, and then try again.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.