以Sia-UI项目的文件组件为例参考阮一峰老师的react-redux入门教程对react-redux的用法总结

过一段时间后的批注:看了慕课网老师的react课程,这篇文章我推荐的链接还是值得看,但我对react的解释就不用看啦,因为部分理解还是不到位,而且没有对中间件的解释,但我是不会删除这个宝藏的,嘻嘻。

阮一峰老师的文章链接:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
Sia-UI的github链接:https://github.com/NebulousLabs/Sia-UI
博客写完也差不多记住了,记住步骤了就理解了,没记住就看不懂,这是我对react-redux的深刻理解,哈哈。

有几个关键名词需要记一下,定义是我理解的写法⊙_⊙
ui组件:只负责美,页面的展示;这部分就是展示数据而已而已啦。
容器组件:只负责脑子,页面的逻辑,如果二者有重合的话,要井水不犯河水,细分细分细分成更小的ui组件和容器组件。这部分就是对数据进行处理,调起action里的事件。
action:组件里所有事件的定义都写在这里,具体就是ui组件里面要调用的事件名字,为什么这样,因为代码就要规规矩矩,整整齐齐,这样才能透出满屏的大佬范;这部分就是让你往东,你不能往西的作用。
reducer:根据用户触发的action具体事件在自己内部找到具体处理方法,更新一些状态的值,再把这些值传给容器组件;这部分就是让你往东,东到具体哪个位置,乱东要挨打的哟。

总体思路就是这样的:
1.先在ui组件里面绑定事件,等待触发
2.用户触发后,关键点,容器组件将触发的事件名匹配mapDispatchToProps这个参数定义的相应事件,触发action
3.action收到通知后,找到事件对应的type类型
4.然后由reducer判断对应的type类型,设置对应的状态值
5.然后由容器组件的mapStateToProps参数更新到对应的view上面
反正我是这么理解的◑﹏◐◑﹏◐◑﹏◐,如果理解错了,下边的文字也是可以看一看的。

一、附一张项目代码结构图

二、file组件目录结构解释

assets/放各种image文件
css/放各种css文件
index.html      --    1.react-root:定义根节点
                             line15--用法:<div id="react-root"></div>

<!DOCTYPE html>
<html>

<head>
	<title>Files</title>
	<link rel='stylesheet' href='../../css/fonts.css'>
	<link rel='stylesheet' href='../../css/font-awesome.min.css'>
	<link rel='stylesheet' href='../../css/plugin-standard.css'>
	<link rel='stylesheet' href='../../css/pure-min.css'>
	<link rel='stylesheet' href='css/files.css'>
</head>

<body>
	<!-- React root -->
	<div id="react-root"></div>
	<script>
		{
			const scripts = [];

			// Dynamically insert the bundled app script in the renderer process
			const port = process.env.PORT || 1212;
			if (process.env.HOT) {
				scripts.push('http://localhost:' + port + '/dist/plugins/Files.dev.js')
			} else {
				scripts.push('../../dist/plugins/Files.prod.js')
				scripts.push('../../dist/commons.prod.js')
			}

			document.write(
				scripts
					.map(script => `<script defer src="${script}"><\/script>`)
					.join('')
			);
		}
	</script>
</body>

</html>

划重点----------------------------------------------------------------------------------------

js/index.js     --     1.createStore from redux:创建store对象,store对象用来保存状态数据
                            line17--用法:const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
                            store.getState('xx');store.dispatch('loadData')。手动获取状态,触发事件,很方便。
                         (1)rootReducer:用户触发一个action,store对象需要对相应的数据进行state更新,然后反映到view组件,这种state的变化是通过reducer处理的。
                         (2)sagaMiddleware为中间件这个处理异步请求的,没有异步请求就不用,这个不懂,省略。
                            2.Provider from react-redux:给容器组件传递state对象,包在最外层,这样App组件内的所有组件都能拿到state对象
                            line21--用法:<Provider store={store}><App /></Provider>
                            3.ReactDOM form react-dom:在根节点渲染react组件
                            line25--用法:ReactDOM.render(rootElement, document.getElementById('react-root'))

import React from 'react'
import ReactDOM from 'react-dom'
import createSagaMiddleware from 'redux-saga'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import rootReducer from './reducers/index.js'
import rootSaga from './sagas/index.js'
import App from './containers/app.js'
import { fetchData } from './actions/files.js'

// If dev enable window reload
if (process.env.NODE_ENV === 'development') {
  require('electron-css-reload')()
}

const sagaMiddleware = createSagaMiddleware()
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga)

const rootElement = (
  <Provider store={store}>
    <App />
  </Provider>
)
ReactDOM.render(rootElement, document.getElementById('react-root'))

// update state when plugin is focused
window.onfocus = () => {
  store.dispatch(fetchData())
}

状态管理解释----------------------------------------------------------------------------------------

js/reducers/index.js     --     1.combineReducers from redux:将多个状态处理合并为一个大的,对于大项目需要各司其职,不然很容易乱
                                            line8-用法:const rootReducer = combineReducers({wallet,files,deletedialog,renamedialog,allowancedialog})

import { combineReducers } from 'redux'
import wallet from './wallet.js'
import files from './files.js'
import deletedialog from './deletedialog.js'
import renamedialog from './renamedialog.js'
import allowancedialog from './allowancedialog.js'

const rootReducer = combineReducers({
  wallet,
  files,
  deletedialog,
  renamedialog,
  allowancedialog
})

export default rootReducer

js/reducers/allowancedialog.js     --    以allowancedialog为例解释具体到某一个reducer的用法
                                                            reducer接收state和action两个参数,state为默认的状态键值对,reducer根据action触发的事件类型设置相应的state状态;action由ui组件中相应的事件触发

import { Map } from 'immutable'
import * as constants from '../constants/files.js'

const initialState = Map({
  storageEstimate: '0 B',
  feeEstimate: 0,
  confirming: false,
  confirmationAllowance: '0'
})

export default function allowancedialogReduceR (state = initialState, action) {
  switch (action.type) {
    case constants.SHOW_ALLOWANCE_CONFIRMATION:
      return state
        .set('confirming', true)
        .set('confirmationAllowance', action.allowance)
    case constants.HIDE_ALLOWANCE_CONFIRMATION:
      return state.set('confirming', false)
    case constants.CLOSE_ALLOWANCE_DIALOG:
      return state.set('confirming', false)
    case constants.SET_FEE_ESTIMATE:
      return state.set('feeEstimate', action.estimate)
    case constants.SET_STORAGE_ESTIMATE:
      return state.set('storageEstimate', action.estimate)
    default:
      return state
  }
}

js/actions/files.js     --    action中每个事件的type类型是必需的,其他参数根据事件需要的参数自定义,引用为action.参数名 

import * as constants from '../constants/files.js'

export const showAllowanceConfirmation = allowance => ({
  type: constants.SHOW_ALLOWANCE_CONFIRMATION,
  allowance
})
export const hideAllowanceConfirmation = () => ({
  type: constants.HIDE_ALLOWANCE_CONFIRMATION
})
export const closeAllowanceDialog = () => ({
  type: constants.CLOSE_ALLOWANCE_DIALOG
})
export const setStorageEstimate = estimate => ({
  type: constants.SET_STORAGE_ESTIMATE,
  estimate
})
export const setFeeEstimate = estimate => ({
  type: constants.SET_FEE_ESTIMATE,
  estimate
})

js/components/allowancedialog.js     --    1.ui组件,负责与用户的交互,传递用户点击的事件和事件参数给reducer
                                                                 2.PropTypes from prop-types:规定参数类型,这些参数的值是action文件里面自定义的其他参数的值,line106

import PropTypes from 'prop-types'
import React from 'react'
import UnlockWarning from './unlockwarning.js'
import ConfirmationDialog from './allowanceconfirmation.js'
import BigNumber from 'bignumber.js'

const AllowanceDialog = ({
  confirming,
  confirmationAllowance,
  unlocked,
  synced,
  feeEstimate,
  storageEstimate,
  actions
}) => {
  const onCancelClick = () => actions.closeAllowanceDialog()
  const onConfirmationCancel = () => actions.hideAllowanceConfirmation()
  const onConfirmClick = () => actions.setAllowance(confirmationAllowance)
  const onAcceptClick = e => {
    e.preventDefault()
    actions.showAllowanceConfirmation(e.target.allowance.value)
  }
  const onAllowanceChange = e => actions.getStorageEstimate(e.target.value)
  const dialogContents = confirming ? (
    <ConfirmationDialog
      allowance={confirmationAllowance}
      onConfirmClick={onConfirmClick}
      onCancelClick={onConfirmationCancel}
    />
  ) : (
    <div className='allowance-dialog'>
      <h3> Buy storage on the Sia Decentralized Network</h3>
      <div className='allowance-message'>
        <p>
          You need to allocate funds to upload and download on Sia. Your
          allowance remains locked for 3 months. Unspent funds are then
          refunded*. You can increase your allowance at any time.
        </p>
        <p>
          Your storage allowance automatically refills every 6 weeks. Your
          computer must be online with your wallet unlocked to complete the
          refill. If Sia fails to refill the allowance by the end of the lock-in
          period, your data may be lost.
        </p>
        <p className='footnote'>
          *contract fees are non-refundable. They will be subtracted from the
          allowance that you set.
        </p>
      </div>
      <form onSubmit={onAcceptClick}>
        <div className='allowance-input'>
          <label>
            Allowance:
            <input
              type='number'
              name='allowance'
              defaultValue='5000'
              onFocus={onAllowanceChange}
              onChange={onAllowanceChange}
              required
              autoFocus
              className='allowance-amount'
            />
          </label>
          <span> SC</span>
        </div>
        <div className='allowance-buttons'>
          <button type='submit' className='allowance-button-accept'>
            Accept
          </button>
          <button
            type='button'
            onClick={onCancelClick}
            className='allowance-button-cancel'
          >
            Cancel
          </button>
        </div>
        <table className='estimates'>
          <tr>
            <td className='estimate-label'>Estimated Fees</td>
            <td className='estimate-content'>
              {new BigNumber(feeEstimate).round(2).toString()} SC
            </td>
          </tr>
          <tr>
            <td className='estimate-label'>Estimated Storage</td>
            <td className='estimate-content'>{storageEstimate}</td>
          </tr>
        </table>
      </form>
    </div>
  )

  return (
    <div className='modal'>
      {unlocked && synced ? (
        dialogContents
      ) : (
        <UnlockWarning onClick={onCancelClick} />
      )}
    </div>
  )
}

AllowanceDialog.propTypes = {
  confirmationAllowance: PropTypes.string.isRequired,
  confirming: PropTypes.bool.isRequired,
  unlocked: PropTypes.bool.isRequired,
  synced: PropTypes.bool.isRequired,
  feeEstimate: PropTypes.number.isRequired,
  storageEstimate: PropTypes.string.isRequired
}

export default AllowanceDialog

js/containers/allowancedialog.js     --    1.容器组件,负责处理用户交互的逻辑
                                                              2.connect from react-redux:将ui组件和容器组件连接起来,接受两个参数:mapStateToProps和mapDispatchToProps。前者负责展示在页面上,后者负责处理用户的响应事件,这个connect是生成与ui组件联系的容器组件,最后的括号里传入对应的ui组件。

import AllowanceDialogView from '../components/allowancedialog.js'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import {
  showAllowanceConfirmation,
  hideAllowanceConfirmation,
  closeAllowanceDialog,
  setAllowance,
  setFeeEstimate,
  getStorageEstimate
} from '../actions/files.js'

const mapStateToProps = state => ({
  unlocked: state.wallet.get('unlocked'),
  synced: state.wallet.get('synced'),
  storageEstimate: state.allowancedialog.get('storageEstimate'),
  feeEstimate: state.allowancedialog.get('feeEstimate'),
  confirmationAllowance: state.allowancedialog.get('confirmationAllowance'),
  confirming: state.allowancedialog.get('confirming')
})
const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(
    {
      getStorageEstimate,
      setFeeEstimate,
      showAllowanceConfirmation,
      setAllowance,
      hideAllowanceConfirmation,
      closeAllowanceDialog
    },
    dispatch
  )
})

const AllowanceDialog = connect(mapStateToProps, mapDispatchToProps)(
  AllowanceDialogView
)
export default AllowanceDialog

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值