简体   繁体   English

如何在react redux中实现自包含组件?

[英]How to implement a self contained component in react redux?

I am building a file manager webui base on react redux(My purpose is to master react and redux through this project) 我正在构建一个基于react redux的文件管理器webui(我的目的是通过这个项目掌握react和redux)

As you know, a file manager need a tree explorer.I want to build a component which can contain it self and each has self state. 如您所知,文件管理器需要一个树资源管理器。我想构建一个可以自包含它的组件,每个组件都有自身状态。 like below: 如下:

TreeNode can contain children which are TreeNode too.Each TreeNode hold its state {path, children_nodes, right .....} , children_nodes is get from server, path is passed by parent. TreeNode也可以包含TreeNode节点。每个TreeNode保持其状态{path, children_nodes, right .....}children_nodes从服务器获取, path由父节点传递。 That's what I imagine. 这就是我的想象。 Struct like: 结构如下:

App:
TreeNode
--TreeNode
----TreeNode
----TreeNode
TreeNode
TreeNode
--TreeNode
TreeNode
--TreeNode
----TreeNode
----TreeNode

But trouble come here, because redux connect store to the tree root, all node under the root receive same state... 但麻烦来了,因为redux connect存储到树根,根下的所有节点都接收相同的状态...

For example, I have a OPEN_NODE action, which is design to trigger getFileList fucntion base this node's path and set this node's state.open to true .(note: getFileList fucntion not implement yet, just give fake data for now) The screen shot: 例如,我有一个OPEN_NODE动作,它设计用于触发getFileList fucntion基于此节点的路径并将此节点的state.open设置为true 。(注意: getFileList fucntion尚未实现,现在只提供假数据)屏幕截图: 在此输入图像描述

Click each element , but states are equal . 单击每个元素,但states are equal

My code: 我的代码:

containers/App.js 集装箱/ App.js

import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Footer from '../components/Footer';
import TreeNode from '../containers/TreeNode';
import Home from '../containers/Home';
import * as NodeActions from '../actions/NodeActions'

export default class App extends Component {

  componentWillMount() {
    // this will update the nodes on state
    this.props.actions.getNodes();
  }

  render() {
    const { nodes } = this.props
    console.log(nodes)
    return (
      <div className="main-app-container">
        <Home />
        <div className="main-app-nav">Simple Redux Boilerplate</div>
        <div>
          {nodes.map(node =>
            <TreeNode key={node.name} info={node} actions={this.props.actions}/>
          )}
        </div>

        {/*<Footer />*/}
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    nodes: state.opener.nodes,
    open: state.opener.open
  };
}


function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(NodeActions, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

containers/TreeNode.js 集装箱/ TreeNode.js

import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import classNames from 'classnames/bind'
import * as NodeActions from '../actions/NodeActions'

export default class TreeNode extends Component {

  constructor(props, context) {
    super(props, context)
    this.props = {
      open: false,
      nodes: [],
      info:{}
    }
  }

  handleClick() {
    let {open} = this.props
    if (open) {
      this.props.actions.closeNode()
    } else {
      this.props.actions.openNode()
    }
  }

  render() {
    const { actions, nodes, info } = this.props
    return (
      <div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
        <a>{info.name}</a>
        {nodes &&
          <div>{nodes.map(node => <TreeNode info={node} />)}</div>
        }
        {!nodes &&
          <div>no children</div>
        }
      </div>
    );
  }
}

TreeNode.propTypes = {
  open:PropTypes.bool,
  info:PropTypes.object.isRequired,
  nodes:PropTypes.array,
  actions: PropTypes.object.isRequired
}

actions/NodeActions.js 动作/ NodeActions.js

import { OPEN_NODE, CLOSE_NODE, GET_NODES } from '../constants/NodeActionTypes';

export function openNode() {
  return {
    type: OPEN_NODE
  };
}

export function closeNode() {
  return {
    type: CLOSE_NODE
  };
}


export function getNodes() {
  return {
    type: GET_NODES
  };
}

reducers/TreeNodeReducer.js 减速器/ TreeNodeReducer.js

import { OPEN_NODE, CLOSE_NODE, GET_NODES } from '../constants/NodeActionTypes';

const initialState = {
  open: false,
  nodes: [],
  info: {}
}

const testNodes = [
  {name:'t1',type:'t1'},
  {name:'t2',type:'t2'},
  {name:'t3',type:'t3'},
]


function getFileList() {
  return {
    nodes: testNodes
  }
}


export default function opener(state = initialState, action) {
  switch (action.type) {
  case OPEN_NODE:
    var {nodes} = getFileList()
    return {
      ...state,
      open:true,
      nodes:nodes
    };
  case CLOSE_NODE:
    return {
      ...state,
      open:false
    };
  case GET_NODES:
    var {nodes} = getFileList()
    return {
      ...state,
      nodes:nodes
    };
  default:
    return state;
  }
}

For complete code, see my github https://github.com/eromoe/simple-redux-boilerplate 有关完整代码,请参阅我的github https://github.com/eromoe/simple-redux-boilerplate

I don't see an example cover such component, and google result nothing helpful. 我没有看到这样的组件的示例封面,谷歌结果没有任何帮助。 Any idea to overcome this? 有什么想法克服这个?

update : I see this How to manage state in a tree component in reactjs 更新 :我看到如何在reactjs中的树组件中管理状态

But the solution is pass the whole tree to state, can not use in file manager. 但解决方案是将整个树传递到状态,不能在文件管理器中使用。

I'm implementing a Github like app using React and Redux. 我正在使用React和Redux实现一个类似Github的应用程序。

For now, I only list repositories and show its files as well as navigate through them. 目前,我只列出存储库并显示其文件以及浏览它们。

I don't know if this is considered a good or bad practice, but this is how I implemented my Tree component. 我不知道这是不是一个好的或坏的做法,但这是我实现我的Tree组件的方式。

Inside each Tree Component, I have a Link to itself. 在每个树组件内部,我有一个自己的链接。 And I pass some data on the route, so I'm able to get the next tree when I render it. 我在路线上传递了一些数据,所以当我渲染时我能够得到下一棵树。

应用

Component 零件

class Tree extends Component {
  constructor(props) {
    super(props);

    this.renderList = this.renderList.bind(this);
  }

  componentWillMount() {
    this.props.getTree(this.props.params.sha);
  }

  componentWillReceiveProps(nextProps) {
    if(nextProps.params.sha !== this.props.params.sha) {
      this.props.getTree(nextProps.params.sha);
    }
  }

  renderList(file) {
    return (
      <tr key={ file.sha }>
        { file.type == 'tree'
       ? <td><Link to={`/repository/${this.props.params.repoName}/tree/${file.path}/${file.sha}`}>{ file.path }</Link></td>
       : <td><Link to={`/repository/${this.props.params.repoName}/blob/${file.sha}/${file.path}`}>{ file.path }</Link></td>}
      </tr>
    )
  }

  render() {
    const treeFile = this.props.tree;
    const fileName = this.props.params.path;

    return (
      <div className="row">
        <h3>{ fileName }</h3>
        <div className="col-md-12">
          <table className="table table-hover table-bordered">
            <tbody>
              { isEmpty(treeFile.tree) ? <tr>Loading</tr> : treeFile.tree.map(this.renderList) }
            </tbody>
          </table>
        </div>
      </div>
    )
  }
}
export default Tree;

Action 行动

const setTree = (tree) => {
  return {
    type: actionTypes.GET_TREE,
    tree
  };
};

export const getTree = (sha) => {

  return (dispatch, getState) => {
    const { repository, profile } = getState();
    const repo = GitHubApi.getRepo(profile.login, repository.name);

    repo.getTree(sha, function(err, data) {
      dispatch(setTree(data));
    });
  }
}

Reducer 减速器

const initialState = "";

export const tree = (state = initialState, action) => {
  switch (action.type) {
    case actionTypes.GET_TREE:
      return getTree(state, action);
  }
  return state;
}

const getTree = (state, action) => {
  const { tree } = action;
  return tree;
}

For the complete code, you can check my repository on github 对于完整的代码,您可以在github上检查我的存储库

https://github.com/glundgren93/Github-redux https://github.com/glundgren93/Github-redux

All of your TreeNode has the same state from redux because your mapStateToProps for all of them are same. 您的所有TreeNode都具有与redux相同的状态,因为它们的mapStateToProps都是相同的。

mapStateToProps can take ownProps (the props of the wrapped component) as second parameter. mapStateToProps可以将ownProps (包装组件的props )作为第二个参数。 You can use it to distinguish your TreeNodes . 您可以使用它来区分您的TreeNodes In your case, path is a good option. 在你的情况下, path是一个很好的选择。

considering writing a state selector like this getChildrenNodes(state, path) and return the nodes accordingly. 考虑编写像getChildrenNodes(state, path)这样的状态选择器并相应地返回节点。

You may want to consider reading through redux docs first, especially this seciont: http://redux.js.org/docs/recipes/ComputingDerivedData.html 您可能需要先考虑阅读redux文档,特别是这个版本: http: //redux.js.org/docs/recipes/ComputingDerivedData.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM