如何在 React Redux 中实现自包含组件?
Posted
技术标签:
【中文标题】如何在 React Redux 中实现自包含组件?【英文标题】:How to implement a self contained component in react redux? 【发布时间】:2016-10-22 11:46:03 【问题描述】:我正在基于react redux构建一个文件管理器webui(我的目的是通过这个项目掌握react和redux)
如您所知,文件管理器需要一个树浏览器。我想构建一个可以包含它自己并且每个都有自己状态的组件。如下:
TreeNode
也可以包含TreeNode
的子代。每个TreeNode
都保持其状态path, children_nodes, right .....
,children_nodes
是从服务器获取的,path
是由父代传递的。这就是我的想象。
结构如下:
App:
TreeNode
--TreeNode
----TreeNode
----TreeNode
TreeNode
TreeNode
--TreeNode
TreeNode
--TreeNode
----TreeNode
----TreeNode
但是麻烦来了,因为reduxconnect
存储到树根,根下的所有节点接收到相同的状态...
例如,我有一个OPEN_NODE
动作,旨在触发getFileList fucntion
基于此节点的路径并将此节点的state.open
设置为true
。(注意:getFileList fucntion
尚未实现,只需给出暂时的假数据)
屏幕截图:
点击每个元素,但states are equal
。
我的代码:
容器/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);
容器/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
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
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;
完整代码见我的githubhttps://github.com/eromoe/simple-redux-boilerplate
我没有看到涵盖此类组件的示例,并且谷歌结果没有任何帮助。 有什么办法克服这个问题吗?
更新: 我看到了这个How to manage state in a tree component in reactjs
但解决方案是将整个树传递给状态,不能在文件管理器中使用。
【问题讨论】:
查看 Redux 中的computing derived data。 【参考方案1】:你所有的TreeNode
与redux 的状态相同,因为你的mapStateToProps
都是相同的。
mapStateToProps
可以将ownProps
(被包装组件的props
)作为第二个参数。你可以用它来区分你的TreeNodes
。在您的情况下,path
是一个不错的选择。
考虑编写像getChildrenNodes(state, path)
这样的状态选择器并相应地返回节点。
您可能需要考虑先阅读 redux 文档,尤其是本节:http://redux.js.org/docs/recipes/ComputingDerivedData.html
【讨论】:
【参考方案2】:我正在使用 React 和 Redux 实现一个类似 Github 的应用程序。
目前,我只列出存储库并显示其文件以及浏览它们。
我不知道这是一种好的做法还是坏的做法,但这就是我实现 Tree 组件的方式。
在每个树组件中,我都有一个指向自身的链接。而且我在路线上传递了一些数据,所以当我渲染它时我能够得到下一棵树。
组件
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;
动作
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));
);
减速器
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;
完整代码可以查看我的github仓库
https://github.com/glundgren93/Github-redux
【讨论】:
以上是关于如何在 React Redux 中实现自包含组件?的主要内容,如果未能解决你的问题,请参考以下文章