所以我对如何集成容器和组件模式感到困惑。我整个上午都在查看示例,似乎没有任何点击。我以前在我的第一个项目中使用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.jsx
或AuthError.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
获取数据?在打破这一点时会有点失落,应该做些什么工作。
答案 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()