了解如何使用 Flummox 实现 Flux 架构
Understanding how to implement a Flux architecture using Flummox
我目前有一个非常简单的 React 应用程序,它使用状态来确定几个视图。单击提交元素会触发对端点的简单调用,传递登录或注册凭据。我可以继续沿着这条路使用 React 和从服务器返回的 JSON 来确定我的观点,但我知道这不是处理数据的正确方式。
我的问题是,从这里开始,我如何使用 Flummox 实现 Flux 模式?我是用负载触发一个动作并在动作或商店中进行 API 调用吗?我试图了解从我的 UI 中的操作到我的 API 调用,再到获得响应和 UI 响应更改的正确方法商店。
下面是我的 Auth 组件的代码。我想看一个关于如何在如此简单的情况下使用 Flummox 的示例,这样我就可以了解扩展和添加功能需要什么。
Auth.jsx
'use strict';
var React = require('react');
var request = require('request');
var Auth = React.createClass({
getInitialState: function() {
return {
name: '',
email: '',
pass: '',
passConfirm: '',
login: true,
register: false
};
},
handleLogin: function(){
this.setState({
login: true,
register: false
});
},
handleRegister: function(){
this.setState({
register: true,
login: false
});
},
handleName: function(event){
this.setState({
name: event.target.value
});
},
handleEmail: function(event){
this.setState({
email: event.target.value
});
},
handlePass: function(event){
this.setState({
pass: event.target.value
});
},
handlePassConfirm: function(event){
this.setState({
passConfirm: event.target.value
});
},
handleSubmit: function(){
var register = {
name: this.state.name,
email: this.state.email,
password: this.state.pass,
confirmPassword: this.state.passConfirm
};
var endpoint = 'http://localhost:3000';
request({
uri: endpoint + '/register',
method: 'POST',
json: register
}, function(error, response, body){
if (error) {
console.log(error);
} else {
console.log(response);
}
});
},
renderLogin: function () {
return (
<form className="login">
<div className="input-container">
<input type='text' value={this.state.email} onChange={this.handleEmail} placeholder="email" />
<input type='password' value={this.state.password} onChange={this.handlePass} placeholder="password" />
</div>
<div className="auth-submit" onClick={this.handleSubmit}>Login</div>
</form>
);
},
renderRegister: function(){
return (
<form className="register">
<div className="input-container">
<input type='text' value={this.state.name} onChange={this.handleName} placeholder="name" />
<input type='email' value={this.state.email} onChange={this.handleEmail} placeholder="email" />
<input type='password' value={this.state.pass} onChange={this.handlePass} placeholder="password" />
<input type='password' value={this.state.passConfirm} onChange={this.handlePassConfirm} placeholder="confirm password" />
</div>
<div className="auth-submit" onClick={this.handleSubmit}>Register</div>
</form>
);
},
render: function(){
var auth = null;
if (this.state.login) {
auth = this.renderLogin();
} else if (this.state.register) {
auth = this.renderRegister();
}
return (
<div className="auth-container">
<div className="auth-logo">Flow</div>
{auth}
<div className="auth-select">
<div className="auth-label-container">
<div className="auth-label" onClick={this.handleLogin}>Login</div>
</div>
<div className="auth-label-container">
<div className="auth-label" onClick={this.handleRegister}>Register</div>
</div>
</div>
</div>
);
},
});
module.exports = Auth;
在深入研究 Flux 之前,我建议清理现有的 React 代码。
首先我会尝试尽可能减少组件自身的状态。例如,您可以使用单个 screen
作为状态,而不是 login
和 register
的单独状态,它接受您正在渲染的实际 screen/view 的字符串值(login/register).
此外,renderLogin()
和 renderRegister()
最好单独作为组件。因此,您的 Auth
组件最终将成为控制器视图。
表单处理同时看起来过于冗长:您已经为每个字段定义了一个单独的处理程序,但是一个 handleChange()
就足够了(您可以将输入元素中的 name="<state-field-name>"
设置为引用您正在引用的状态字段)。
假设您可以开始使用 Flummox 构建通量模式并将您的应用程序状态(而不是本地组件状态)移动到商店中。
我假设您有 read the docs, but as a summary Flummox offers Store
and Action
个助手来为您的应用程序数据(真实来源)和 UI 操作建模。
动作模块看起来像这样:
import { Actions } from 'flummox';
export default class AuthActions extends Actions {
gotoScreen(screenName) {
return screenName;
}
register(requestBody) {
// Here you deal with your authentication API,
// make async requests and return a promise.
// This part is specific to your backend.
return AuthAPI.register(requestBody)
.then(
(value) => Promise.resolve(value),
(reason) => Promise.reject(reason)
);
}
}
而 AuthStore
将是真实的来源,即处理实际的应用程序状态。这注册了动作,所以它知道如何在动作发出变化时更新自己。
import { Store } from 'flummox';
export default class AuthStore extends Store {
constructor(flux) {
super();
let authActions = flux.getActions('auth');
// Here you register the functions which will take care
// of handling the actions and update the store's state.
// These can be sync or async.
this.register(authActions.gotoScreen, this.handleGotoScreen);
this.registerAsync(authActions.register, this.handleRegisterBegin,
this.handleRegisterSuccess,
this.handleRegisterError);
// This is the initial state of your flux store
this.state = {
screen: 'login',
// ... any extra state your app might need
};
}
/* Screen handling */
handleGotoScreen(screenName) {
this.setState({screen: screenName});
}
/* Register */
handleRegisterBegin(requestBody) {
this.setState({
// change state based on action
});
}
handleRegisterSuccess() {
this.setState({
// change state based on action
});
}
handleRegisterError(errors) {
this.setState({
// change state based on action
});
}
}
这些需要封装在一个 Flux
对象中,然后可以从您应用程序的任何地方引用该对象。
import { Flummox } from 'flummox';
import AuthActions from './AuthActions';
import AuthStore from './AuthStore';
export default class Flux extends Flummox {
constructor() {
super();
this.createActions('auth', AuthActions);
this.createStore('auth', AuthStore, this);
}
}
但是您的视图如何了解商店的状态? preferred way to glue your stores with views is by using the FluxComponent
, which would receive the store's state via props when using FluxComponent
.
FluxComponent
接收您应用的 Flux
对象的一个实例,您还需要指定它要连接到哪些商店。
import Flux from './Flux';
let flux = new Flux();
<FluxComponent flux={flux} connectToStores={['auth']}>
<Auth />
</FluxComponent>
状态作为 props 传递,您可以像正常使用 props 一样直接引用值。
同时,这允许访问基础视图中的 flux
道具,这使我们能够访问操作,因此能够在用户交互发生时触发它们:
// You can fire actions from anywhere in your component
this.props.flux.getActions('auth').gotoScreen('register')
由此完成(或开始)通量循环。
我目前有一个非常简单的 React 应用程序,它使用状态来确定几个视图。单击提交元素会触发对端点的简单调用,传递登录或注册凭据。我可以继续沿着这条路使用 React 和从服务器返回的 JSON 来确定我的观点,但我知道这不是处理数据的正确方式。
我的问题是,从这里开始,我如何使用 Flummox 实现 Flux 模式?我是用负载触发一个动作并在动作或商店中进行 API 调用吗?我试图了解从我的 UI 中的操作到我的 API 调用,再到获得响应和 UI 响应更改的正确方法商店。
下面是我的 Auth 组件的代码。我想看一个关于如何在如此简单的情况下使用 Flummox 的示例,这样我就可以了解扩展和添加功能需要什么。
Auth.jsx
'use strict';
var React = require('react');
var request = require('request');
var Auth = React.createClass({
getInitialState: function() {
return {
name: '',
email: '',
pass: '',
passConfirm: '',
login: true,
register: false
};
},
handleLogin: function(){
this.setState({
login: true,
register: false
});
},
handleRegister: function(){
this.setState({
register: true,
login: false
});
},
handleName: function(event){
this.setState({
name: event.target.value
});
},
handleEmail: function(event){
this.setState({
email: event.target.value
});
},
handlePass: function(event){
this.setState({
pass: event.target.value
});
},
handlePassConfirm: function(event){
this.setState({
passConfirm: event.target.value
});
},
handleSubmit: function(){
var register = {
name: this.state.name,
email: this.state.email,
password: this.state.pass,
confirmPassword: this.state.passConfirm
};
var endpoint = 'http://localhost:3000';
request({
uri: endpoint + '/register',
method: 'POST',
json: register
}, function(error, response, body){
if (error) {
console.log(error);
} else {
console.log(response);
}
});
},
renderLogin: function () {
return (
<form className="login">
<div className="input-container">
<input type='text' value={this.state.email} onChange={this.handleEmail} placeholder="email" />
<input type='password' value={this.state.password} onChange={this.handlePass} placeholder="password" />
</div>
<div className="auth-submit" onClick={this.handleSubmit}>Login</div>
</form>
);
},
renderRegister: function(){
return (
<form className="register">
<div className="input-container">
<input type='text' value={this.state.name} onChange={this.handleName} placeholder="name" />
<input type='email' value={this.state.email} onChange={this.handleEmail} placeholder="email" />
<input type='password' value={this.state.pass} onChange={this.handlePass} placeholder="password" />
<input type='password' value={this.state.passConfirm} onChange={this.handlePassConfirm} placeholder="confirm password" />
</div>
<div className="auth-submit" onClick={this.handleSubmit}>Register</div>
</form>
);
},
render: function(){
var auth = null;
if (this.state.login) {
auth = this.renderLogin();
} else if (this.state.register) {
auth = this.renderRegister();
}
return (
<div className="auth-container">
<div className="auth-logo">Flow</div>
{auth}
<div className="auth-select">
<div className="auth-label-container">
<div className="auth-label" onClick={this.handleLogin}>Login</div>
</div>
<div className="auth-label-container">
<div className="auth-label" onClick={this.handleRegister}>Register</div>
</div>
</div>
</div>
);
},
});
module.exports = Auth;
在深入研究 Flux 之前,我建议清理现有的 React 代码。
首先我会尝试尽可能减少组件自身的状态。例如,您可以使用单个 screen
作为状态,而不是 login
和 register
的单独状态,它接受您正在渲染的实际 screen/view 的字符串值(login/register).
此外,renderLogin()
和 renderRegister()
最好单独作为组件。因此,您的 Auth
组件最终将成为控制器视图。
表单处理同时看起来过于冗长:您已经为每个字段定义了一个单独的处理程序,但是一个 handleChange()
就足够了(您可以将输入元素中的 name="<state-field-name>"
设置为引用您正在引用的状态字段)。
假设您可以开始使用 Flummox 构建通量模式并将您的应用程序状态(而不是本地组件状态)移动到商店中。
我假设您有 read the docs, but as a summary Flummox offers Store
and Action
个助手来为您的应用程序数据(真实来源)和 UI 操作建模。
动作模块看起来像这样:
import { Actions } from 'flummox';
export default class AuthActions extends Actions {
gotoScreen(screenName) {
return screenName;
}
register(requestBody) {
// Here you deal with your authentication API,
// make async requests and return a promise.
// This part is specific to your backend.
return AuthAPI.register(requestBody)
.then(
(value) => Promise.resolve(value),
(reason) => Promise.reject(reason)
);
}
}
而 AuthStore
将是真实的来源,即处理实际的应用程序状态。这注册了动作,所以它知道如何在动作发出变化时更新自己。
import { Store } from 'flummox';
export default class AuthStore extends Store {
constructor(flux) {
super();
let authActions = flux.getActions('auth');
// Here you register the functions which will take care
// of handling the actions and update the store's state.
// These can be sync or async.
this.register(authActions.gotoScreen, this.handleGotoScreen);
this.registerAsync(authActions.register, this.handleRegisterBegin,
this.handleRegisterSuccess,
this.handleRegisterError);
// This is the initial state of your flux store
this.state = {
screen: 'login',
// ... any extra state your app might need
};
}
/* Screen handling */
handleGotoScreen(screenName) {
this.setState({screen: screenName});
}
/* Register */
handleRegisterBegin(requestBody) {
this.setState({
// change state based on action
});
}
handleRegisterSuccess() {
this.setState({
// change state based on action
});
}
handleRegisterError(errors) {
this.setState({
// change state based on action
});
}
}
这些需要封装在一个 Flux
对象中,然后可以从您应用程序的任何地方引用该对象。
import { Flummox } from 'flummox';
import AuthActions from './AuthActions';
import AuthStore from './AuthStore';
export default class Flux extends Flummox {
constructor() {
super();
this.createActions('auth', AuthActions);
this.createStore('auth', AuthStore, this);
}
}
但是您的视图如何了解商店的状态? preferred way to glue your stores with views is by using the FluxComponent
, which would receive the store's state via props when using FluxComponent
.
FluxComponent
接收您应用的 Flux
对象的一个实例,您还需要指定它要连接到哪些商店。
import Flux from './Flux';
let flux = new Flux();
<FluxComponent flux={flux} connectToStores={['auth']}>
<Auth />
</FluxComponent>
状态作为 props 传递,您可以像正常使用 props 一样直接引用值。
同时,这允许访问基础视图中的 flux
道具,这使我们能够访问操作,因此能够在用户交互发生时触发它们:
// You can fire actions from anywhere in your component
this.props.flux.getActions('auth').gotoScreen('register')
由此完成(或开始)通量循环。