在容器组件模式中集成调度操作

时间:2017-08-31 20:09:01

标签: reactjs redux react-redux

所以我对如何集成容器和组件模式感到困惑。我整个上午都在查看示例,似乎没有任何点击。我以前在我的第一个项目中使用React的方法是获取视图组件中的数据,然后使用@connect将这些数据作为道具传递下去,但是此时以“自动”的方式传递给我。

import React;
...
import {action} from 'path/to/action.js';

@connect((store) => {return{ key: store.property}});

export class Component{
  componentWillMount(){
    this.props.dispatch(action());
  }
}

由于我正在使用React,我想学习使用Redux构建更“正确”的方法,并在更深层次上了解正在发生的事情。

我的设置是

index.jsx (This renders all of my HOCs)
  |    
  App.jsx (Container)
    |
    Auth.jsx (Component)
      |
      Layout.jsx (Component) - Contains app content
      --or--
      AuthError.jsx (Component) - 401 unauthenticated error page

身份验证是通过外部资源处理的,因此此应用程序无法通过登录或注销来控制任何内容。没有登录/退出状态只是从API接收标识用户角色和对象的对象。认证布尔值。

我想要发生的是,当App加载时,它将从模拟API JSON Server获取数据。从那里它将呈现Auth组件。 Auth组件将从props接收App.jsx,并呈现Layout.jsxAuthError.jsx

我遇到的问题是如何整合这些问题。我将省略我认为不完全与该问题有关的代码行。

store.js

import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunk from 'redux-thunk';
import { createLogger } from 'redux-logger';
import promise from 'redux-promise-middleware';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers';

const middleware = applyMiddleware(promise(), thunk, createLogger());

export default createStore(reducer, composeWithDevTools(middleware));

index.jsx

import React from 'react';
import store from './store.js';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import App from './containers/App.jsx';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
document.getElementById('root')
);

App.jsx

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { authenticateUser } from '../actions/authActions.js';
import Auth from '../components/Auth.jsx';

class App extends Component {
  constructor(props) {
    super(props);
      this.state = {
        authenticated: false // this needs to be set
      };
  }
  componentWillMount() {
    console.log('APP PROPS', this.props);
    // this.props.actions.authenticateUser();
    authenticateUser(); // this runs but doesn't run the dispatch function
    // What I think needs to happen here Dispatch an Action and then setState referring back to how I would previous build with React Redux.
  }
  render() {
    return (
        <Auth app_name={ApplicationName} authenticated={this.state.authenticated} {...this.props} />
    );
  }
}

const mapStateToProps = state => {
  console.log('redux store auth state', state);
  return {
    auth: state.auth
  };
};

const mapDispatchToProps = dispatch => {
  return { actions: bindActionCreators(authenticateUser, dispatch) };
};

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

Auth.jsx

import React from 'react';
import { Route } from 'react-router-dom';

import AuthError from './AuthError.jsx';
import Layout from './Layout.jsx';

export default function Auth(props) {
  console.log('AUTH PROPS', props);
  const renderLayout = () => {
    if (props.authenticated == true) {
        return <Layout app_name={props.app_name} />;
    } else {
        return <AuthError />;
    }
  };
  return <Route path="/" render={renderLayout} />;
}

authReducer.js

export default function reducer(
  state = {
    authenticated: null
  },
  action
) {
switch (action.type) {
  case 'AUTH_SUCCESSFUL': {
    return {
      ...state,
        authenticated: action.payload.authenticated
    };
    break;
    }
    case 'AUTH_REJECTED': {
      return {
        ...state,
        authenticated: false
      };
    }
  }
  return state;
}

authActions.js

import axios from 'axios';

export function authenticateUser() {
console.log('authenticate user action has been called');
return function(dispatch) {
  // nothing runs within this block so it's leading me to believe nothing is being `dispatch`ed
    console.log('dispatch', dispatch);
    axios
        .get('localhost:3004/auth')
        .then(response => {
          dispatch({ type: 'AUTH_SUCCESSFUL', payload: response.data });
            console.log('response', response);
        })
        .catch(err => {
            dispatch({ type: 'AUTH_REJECTED', payload: err });
            console.log('error', err);
        });
    };
}

现在App.jsx内部我可以控制authReducer的状态,我可以在我的操作中调用authenticateUser()。但是当我调用authenticateUser()时,返回调度功能不会运行。我应该在App.jsx中调度身份验证操作吗?或者我应该在Auth.jsx作为道具调度auth,然后让App.jsx获取数据?在打破这一点时会有点失落,应该做些什么工作。

2 个答案:

答案 0 :(得分:1)

我会做一个简短的解释,帮助你理解这些模式,不要再混淆了(我希望)。

所以,让我们暂时忘记减速器,专注于容器动作创建者组件模式。

组件

很多人在使用 redux 应用程序时以错误的方式实现组件。

对于redux,更好的组件方法是使用无状态模式实现它(请参阅Functional Components)。我们在实践中看到:

// components/Subscribe.js

import React from 'react'
import PropTypes from 'prop-types'

const Subscribe = ({text, confirmSubscription}) =>
  <div>
    <p>{text}</p>
    <button onClick={confirmSubscription}>Confirm</button>
  </div>

Subscribe.propTypes = {
  subtitle: PropTypes.string.isRequired
}

Subscribe.defaultProps = {
  subtitle: ''
}

export default Subtitle

这使您可以优化组件占用空间,因为它们的功能少于有状态组件(或类组件),因此您将获得一些性能并继续专注于组件目标。

容器

另一方面, Container 是一种具有一定逻辑实现的组件。容器是为绑定 React Redux 而创建的模式,因为两者都不应直接交互。这意味着,Container渲染组件,处理一些组件事件(例如,表单onSubmit)并使用应用程序状态提供组件。因此,Container是与Redux交互的最佳位置。 (react-redux)[https://github.com/reactjs/react-redux] Redux 使这项任务更容易一些。因此,在Subscribe组件上提供和捕获交互的简单容器可能是这样的:

// containers/SubscribeContainer.js

import React from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'

import { confirmSubscription } from 'actions/subscription'
import Subscribe from 'components/Subscribe'

const mapStateToProps = state => ({
  text: state.subscription.text
})

const mapDispatchToProps = dispatch =>
  bindActionCreators({
    confirmSubscription
  }, dispatch)

const Container = connect(mapStateToProps, mapDispatchToProps)
export default Container(Subscribe)

动作创作者

动作创建者(或动作创建者)只是返回动作的集合或功能。很简单:

// actions/subscription

export const CONFIRM_SUBSCRIPTION = 'actions.confirmSubscription'

export function confirmSubscription() {
  return {
    type: CONFIRM_SUBSCRIPTION
  }
}

目前,我们已经实施了三合一模式,组件容器动作创建器,从这里,您只需要两件事使用 Redux

  • 创建subscription商店。
  • 处理CONFIRM_SUBSCRIPTION(以便更新应用的状态)
  • 返回新状态

当您从任何reducer返回一个新状态时会发生魔法,mapStateToProps将被调用,您将收到新状态作为参数,从那里,React将在必要时更新您的组件,如果是这些组件是无状态的,PureComponent(仅适用于单级状态和道具)或自定义shouldComponentUpdate

要记住的另一件事是不要在组件容器操作创建器中执行提取或异步执行,相反,您可以使用像redux-thunk这样的中间件组成一个自定义的middeware来捕获动作并在发送给reducers之前处理它们。

答案 1 :(得分:0)

您的authenticateUser返回一个函数,您需要从字面上运行该函数。正确的方法是在mapDispatchToProps中添加一个属性

const mapDispatchToProps = dispatch => { return { authenticateUser: () => dispatch(authenticateUser()) }; }; 然后,在你的componentWillMount函数中,调用 this.props.authenticateUer()

Check this