[英]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.