2017-02-06 17 views
15

我有一个父组件调用App.jsReactJS + Redux:如何将动作创建者构建到每个组件?

... 

render() { 
    return (
    <div> 
     {React.cloneElement(this.props.children, this.props} 
    </div> 
) 
} 

... 

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

export default connect(
    ..., 
    mapDispatchToProps 
)(App) 

而且道具将被传递到每个组件。我希望每个组件都有自己的动作创建者文件,但是如何将所有动作创建者合并为一个动作创建者,以便可以从App.js级别传递动作创建者?任何其他建议,也将赞赏行动创作者到每个组件。

这是迄今结构:

ComponentOne 
..actions.js //action creators 
..ComponentOne.js 
ComponentTwo 
..actions.js //action creators 
..ComponentTwo.js 
App.js 
actions.js//should I compile all the action creators here? 

而且每个actions.js会像这样进行:

let actions = { 
    logSayings() { 
    ... 
    } 
} 

export default actions 

预先感谢您,并给予好评/接受的答案。

REDUX设立

store.js

import { applyMiddleware, compose, createStore } from 'redux' 
import rootReducer from './reducers/rootReducer' 
import logger from 'redux-logger' 
import thunk from 'redux-thunk' 

let finalCreateStore = compose(
    applyMiddleware(thunk, logger()) 
)(createStore) 

export default function configureStore(initialState = {articles: []}) { 
    return finalCreateStore(rootReducer, initialState) 
} 

actions.js

import { hashHistory } from 'react-router' 
import { browserHistory } from 'react-router'; 

let actions = { 
    updateBar(status) { 
    return { 
     type: 'UPDATE_BAR', 
     indicator: status 
    } 
    } 
} 

export default actions 

homeReducer.js

const homeReducer = function(articles = [], action){ 
    switch(action.type){ 
    case 'UPDATE_BAR': 
     return { 
     indicator: action.indicator, 
     } 

    default: 
     return articles 
    } 
} 

export default homeReducer 

index.js

import React from 'react'; 
import {render} from 'react-dom'; 
import configureStore from '../../redux/store' 
import { Provider } from 'react-redux' 
import { Router, Route, IndexRoute, hashHistory } from 'react-router' 

import App from './components/App' 
import Home from './components/Home/Home' 

let initialState = { 

} 

let store = configureStore(initialState) 

render(
    <div> 
    <Provider store={store}> 
     <Router history={hashHistory}> 
     <Route 
      component={App} 
      path='/' 
     > 
      <IndexRoute component={Home}/> 
     </Route> 
     </Router> 
    </Provider> 
    </div>, 
    document.querySelector('.wrapper') 
) 
+0

为什么你想要所有的孩子组件都有动作创建者?为什么你不能集中你的动作在App的mapDispatchToProps上? – Robsonsjre

+0

@Robsonsjre我已经这样做了,但它已经到了一个过于集群的地步。太多的动作创作者,我将不得不始终浏览不相关的动作,找到我需要的动作。拥有特定组件的动作创建者可帮助我查找动作创建者,并更快地采取行动。 –

回答

1

我能想到的两种方法:于一身的A​​pp.js的mapDisatchToProps

  • 结合您的actionObjects
  • 您的每一个组件都可以成为一个 '容器' 组件

示例1:

App.js 

import actionObj1 from '../actionComponent1' 

export default connect(
    mapStateToProps, 
    Object.assign({}, actionObj1, actionObj2, actionObj3) 
)(App) 

UPDATE(每个子组件成为容器,只需连接就像App.js):

Component1.js 

export default connect(
    mapStateToProps, 
    actionObj1) 
)(Component1) 
+0

您能否详细说明如何让每个组件成为“容器”组件?我可以将所有动作创建者表单合并为一个动作创建者表单,而不是导入每个动作创建者表单?在相同的逻辑中有没有更好的方法? –

+0

只需登录即可查看您是否看过我以前的评论。请告诉我。 –

1

我觉得这事,你可能想看看是改变你的终极版文件夹结构更加模块化。您不会拥有臃肿的动作文件,并且它应该比包含所有redux动作的大动作/类型/缩小器文件更容易维护。

我喜欢我的终极版的行为,种类和减速器分成模块以下列方式:

在我src文件夹中,我将有一个modules文件夹。在这个modules文件夹中,我将在应用程序状态下为不同类型的数据创建子文件夹。例如,auth的文件夹,posts的文件夹和comments的文件夹。我也会有一个index.js

在每个文件夹中,我将有一个actions.js文件,一个types.js文件和一个reducer.js文件。

在每个action.js文件中,我只会在与该类型数据相关的操作中进行放置。

例如,在actions.js我的文件夹posts的,我将有动作的创造者listPostsgetPost,等我的行动创造者的结构是什么,你可能会看到,因为我用的是自定义的中间件略有不同,但您可以用作灵感或复制的中间件的一个很好的例子是:react-redux-universal-hot-example。尽管他们更喜欢将所有动作创建者,类型和缩减器放在组合文件中,但我喜欢将它们分开。

import types from './types'; 

export const getPost = id => ({ 
    type: types.LOAD_POST_DETAIL, 
    responseTypes: [types.LOAD_POST_DETAIL_SUCCESS, types.LOAD_POST_DETAIL_FAILURE], 
    promise: client => client.get(`/posts/${id}`), 
}); 

export const listPosts =() => ({ 
    type: types.LIST_POSTS, 
    responseTypes: [types.LIST_POSTS_SUCCESS, types.LIST_POSTS_FAILURE], 
    promise: client => client.get('/posts'), 
}); 

在每个types.js文件中,我只会放入与该类型数据相关的类型。 例如,我的types.js文件可能是这样的:

export default { 
    LOAD_POST_DETAIL: 'LOAD_POST_DETAIL', 
    LOAD_POST_DETAIL_SUCCESS: 'LOAD_POST_DETAIL_SUCCESS', 
    LOAD_POST_DETAIL_FAILURE: 'LOAD_POST_DETAIL_FAILURE', 

    LIST_POSTS: 'LIST_POSTS', 
    LIST_POSTS_SUCCESS: 'LIST_POSTS_SUCCESS', 
    LIST_POSTS_FAILURE: 'LIST_POSTS_FAILURE', 

    UPDATE_POST: 'UPDATE_POST', 
    UPDATE_POST_SUCCESS: 'UPDATE_POST_SUCCESS', 
    UPDATE_POST_FAILURE: 'UPDATE_POST_FAILURE', 
}; 

我减速将只针对该类型的数据减速功能。

reducer.js文件在我posts文件夹中的一个例子:

import { combineReducers } from 'redux'; 
import type { Reducer } from 'redux'; 
import { get, assign } from 'lodash'; 
import types from './types'; 

const all = (
    state = { isFetching: false, data: [] }, 
    action, 
) => { 
    switch (action.type) { 
    case types.LIST_POSTS: 
     return assign({}, state, { isFetching: true }); 
    case types.LIST_POSTS_SUCCESS: 
     return assign({}, state, get(action, 'result.data'), { isFetching: false }); 
    default: return state; 
    } 
}; 

export const reducer = combineReducers({ all }); 

modules文件夹的index.js,您可以导出所有的减速器(要合并,当你创建你的终极版店),像这样:

export { reducer as auth } from './auth/reducer'; 
export { reducer as posts } from './posts/reducer'; 
export { reducer as comments } from './comments/reducer'; 

当你import一个动作,你只会做这样的事情

import { listPosts } from 'modules/posts/actions'; 

设置您的redux文件夹结构以具有更多模块化设计,可以节省您很多头痛的问题,因为函数的放置位置以及从哪里导入函数都有一个清晰的模式。

+0

欣赏你的repsonse呦。所有的动作创造者将被放置在哪里? –

+0

@JoKo,我更新了自己的答案,以便在actions.js文件中给出一些动作创建者的示例。请注意,我的动作创建者的结构与您通常看到的略有不同,但这是因为我使用的是基于链接链接的自定义中间件,但它对于我的实现非常具体,因此在功能上没有显着不同我链接的那个。我还在承诺/客户端中间件上添加了一些注释,并发布了一个链接,指向您可以从中获得灵感的示例中间件,以及商店项目的模块化分离的链接示例。 –

6

这是我通常构建我的react/reducex应用程序的方式。

我在组件&容器外面执行操作。我通常会尝试将我的动作文件命名为特定于我正在构建的应用程序的某些区域。例如UsersActions,ProductActions,OrdersActions,CheeseActions工作太...

App 
    actions 
    CheeseActions.js 
    ... 
    components 
    CheeseBox.js 
    containers 
    CheeseApp.js 
    ... 

例容器 - 让容器手柄的操作。 (有点像一个控制器。)

// import the actions that this container needs to do it's job. 
import { makeCheese, sellCheese } from '../actions/CheeseActions' 
import CheeseBox from '../components/CheeseBox' 

class CheeseApp extends Component { 

    // Let the container handle the actions 
    onPurchasePressed(kind) { 
    dispatch(sellCheese(kind)) 
    } 

    // More actions... 

    render() { 
    return(
     <CheeseBox onPurchase={this.onPurchasePressed.bind(this)} /> 
    ) 
    } 

} 

... 

export default connect(mapStateToProps)(CheeseApp) 

示例组件 - 我们将让使用this.props.onPurchase(“切达”)的集装箱处理功能, 。

// Cheesebox is dumb, keep him that way. 
export default class CheeseBox extends Component { 

    static propTypes = { 
    onPurchase: PropTypes.func.isRequired, 
    } 

    render() { 
    const { onPurchase } = this.props 
    return(
     <p>Hi, I love cheese.</p> 
     <a onClick={() => onPurchase('Cheddar')} />Buy Now!</a> 
    ) 
    } 

} 

希望这会有所帮助。如果您有任何问题,请告诉我。

5

的一种方式做到这一点是怎么回事模块化特性明智的。这样的代码将是更清洁和可读性。

App 
    action/ 
    component1Actions.js 
    ... 
    componentnActions.js 
    actionTypes.js 
    components/ 
    Component1.js 
    ... 
    Componentn.js 
    container/ 
    Component1.js 
    ... 
    Componentn.js 
    reducers/ 
    component1Reducer.js 
    ... 
    componentnReducer.js 

上面所示的结构是大多数的我也遇到过使用了(我更喜欢这种方法)的开发者。这是有道理的,因为我们分开每个文件都基于该文件的性质。这种方法适用于没有那么多单独文件的中小型项目。

在大型应用程序中,维护代码通常很困难。

另一派是怎么回事domain-wise

app/ 
    ... 
    App.js 
    reducers.js 
    routes.js 
    product/ 
    Product.js 
    ProductContainer.js 
    productReducer.js 
    ... 
    user/ 
    User.js 
    UserContainer.js 
    UserActions.js 
    userReducer.js 
    ... 

这样做的好处是,我们根据其领域单独的文件。对于例如一个应用需要一个用户组件。如果我们将与该域相关的所有文件保存在一个目录下,会更容易。这将使应用程序结构在大型应用程序中更清洁。

两者都有好处。在一天结束时,结构并不重要。这更多是个人选择。

+0

真心感谢洞察!试图根据其域名和目前的设置来分离文件,我遇到了麻烦。我已经使用当前的redux设置更新了原始帖子。减速器是分开的,但行动不是。你介意如何修改设置,使其基于其领域的工作?先谢谢你! –

+0

查看本回购https://github.com/koshuang/react-redux-starter-kit。这可能会提供这个结构的更好的解释。 – DroidNoob