2016-06-21 5 views
3

Я создаю файловый менеджер webui base на response redux (моя цель - освоить реакцию и уменьшить этот проект)Как реализовать автономный компонент в реакции редукции?

Как известно, файловому менеджеру нужен древоискатель. Я хочу построить компонент, который может содержать его сам, и каждый имеет собственное состояние. как показано ниже:

TreeNode может содержать детей, которые TreeNode too.Each TreeNode держать его состояние {path, children_nodes, right .....}, children_nodes это получить от сервера, path передается от родителей. Вот что я себе представляю. Struct как:

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

Но беда приходят сюда, потому что перевождь connect магазин корень дерева, все узел под корень получить то же самое состояние ...

К примеру, у меня есть OPEN_NODE действие, которое является дизайн для запуска getFileList fucntion базового пути этого узла и установить данный узел state.open к true (примечанию: getFileList fucntion еще не реализовать, просто дать ложные данные сейчас). на снимке экрана: enter image description here

Нажмите каждый элемент, но 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 
} 

действия/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 
    }; 
} 

редукторы/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; 
    } 
} 

Для полного кода, см моего GitHub https://github.com/eromoe/simple-redux-boilerplate

Я не вижу пример сопроводительного такой компонента, и Google результат ничего не полезно. Любая идея преодолеть это?

обновление: Я вижу это How to manage state in a tree component in reactjs

Но решение передать все дерево в состояние, не может использовать в файловом менеджере.

+0

Посмотрите на [вычисления полученных данных] (http://redux.js.org/docs/recipes/ComputingDerivedData.html) в Redux. –

ответ

1

Я использую приложение Github, использующее React и Redux.

Пока что я только перечисляю репозитории и показываю их файлы, а также просматриваю их.

Я не знаю, считается ли это хорошей или плохой практикой, но так я реализовал свой компонент Tree.

Внутри каждого компонента дерева у меня есть ссылка на себя. И я передаю некоторые данные по маршруту, поэтому я могу получить следующее дерево, когда я его создаю.

App

Компонент

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

0

Все ваш TreeNode имеет такое же состояние из перевождь, потому что ваш mapStateToProps для всех из них одинаков.

mapStateToProps может принимать ownProps (props обернутого компонента) в качестве второго параметра. Вы можете использовать его, чтобы отличить TreeNodes. В вашем случае path - хороший вариант.

Рассматривая запись такого селектора состояний getChildrenNodes(state, path) и возвращаем узлы соответственно.

Вы можете рассмотреть вопрос о чтении через Redux документы первой, особенно это seciont: http://redux.js.org/docs/recipes/ComputingDerivedData.html

Смежные вопросы