Javascript 管理angular2应用程序中的状态-副作用?
这是一个更一般的问题,但基于维克多·萨夫金邮报Javascript 管理angular2应用程序中的状态-副作用?,javascript,angular,rxjs,Javascript,Angular,Rxjs,这是一个更一般的问题,但基于维克多·萨夫金邮报 让我们考虑使用RXJS:< /P>描述的方法。 interface Todo { id: number; text: string; completed: boolean; } interface AppState { todos: Todo[]; visibilityFilter: string; } function todos(initState: Todo[], actions: Observable<Action>): Ob
让我们考虑使用RXJS:< /P>描述的方法。
interface Todo { id: number; text: string; completed: boolean; }
interface AppState { todos: Todo[]; visibilityFilter: string; }
function todos(initState: Todo[], actions: Observable<Action>): Observable<Todo[]> {
return actions.scan((state, action) => {
if (action instanceof AddTodoAction) {
const newTodo = {id: action.todoId, text: action.text, completed: false};
return [...state, newTodo];
} else {
return state;
}
}, initState);
}
现在我有两个副作用:
function todos(initState: Todo[], actions: Observable<Action>): Observable<Todo[]> {
return actions.scan((state, action) => {
if (action instanceof AddTodoAction) {
const newTodo = {id: action.todoId, text: action.text, completed: false};
actions.onNext(new ParseTodoAction(action.todoId));
return [...state, newTodo];
} else if (action instanceOf ParseTodoAction){
const todo = state.find(t => t.todoId === action.todoId)
parserService
.parse(todo.todoId, todo.text)
.subscribe(r => actions.onNext(new TodoParsedAction(todo.todoId, r.coordinates, r.date)))
} else {
return state;
}
}, initState);
}
但这感觉有点不对劲-不应该所有事情都由操作来管理,我是否应该始终循环所有todo项?您的选项1不能按说明工作:
操作
是一个可观察的可观察的是只读的,onNext
不是该API的一部分。您需要一个观察员来支持选项1。这突出了选项1的真正缺陷:您的状态函数(与Redux reducer相同)需要纯粹且无副作用。这意味着他们不能也不应该采取更多行动
现在在您引用的博客文章中,代码确实传递了一个主题,它既是观察者又是可观察的。所以你可能有一个onNext。但我可以告诉你,当你处理某个主题发布的数据时,递归地向该主题发布数据会让你陷入无尽的麻烦,而且很少值得为正确工作而头疼
在Redux中,调用后端处理来丰富您的状态的典型解决方案是在您已经决定调度AddTodo
时,在开始时调度多个操作。这通常可以通过使用redux thunk和调度函数作为“智能操作”来实现:
而不是:
export function addToDo(args) {
return new AddToDoAction(args);
}
你会:
export function addToDo(args) {
return (dispatch) => {
dispatch(new AddToDoAction(args)); // if you want to dispatch the Todo before parsing
dispatch(parseToDo(args)); // handle parsing
};
}
export function parseToDo(args) {
return (dispatch) => {
if (thisToDoNeedsParsing(args)) {
callServerAndParse(args).then(result => {
// dispatch an action to update the Todo
dispatch(new EnrichToDoWithParsedData(result));
});
}
};
}
// UI code would do:
dispatch(addToDo(args));
UI发送一个智能操作(thunk),该操作将发送AddToDoAction
,以在您的状态下获取未解析的todo(如果需要,您的UI可以选择在解析完成之前不显示它)。然后,它将发送另一个智能操作(thunk),该操作将实际调用服务器以获取更多数据,然后将结果发送一个EnrichToDoWithParsedData
操作,以便更新Todo
至于日历的更新……您可能可以使用上面的模式(在addToDo
和parseToDo
中插入对possiblyUpdateCalendar()
的调用),这样,如果todo包含了您需要的所有内容,它就可以更新日历,并在完成后调度一个操作,将todo标记为已添加
现在,我展示的这个示例是特定于Redux的,我不认为您正在使用的基于RxJs的示例有任何类似于thunk的东西。在您的方案中添加此支持的一种方法是在主题中添加一个flatMap
操作符,如下所示:
interface Todo {
id: number;
text: string;
completed: boolean;
location?: Coordinates;
date?: Date;
inCalendar?: boolean;
parsed?: boolean;
}
let actionStream = actionsSubject.flatMap(action => {
if (typeof action !== "function") {
// not a thunk. just return it as a simple observable
return Rx.Observable.of(action);
}
// call the function and give it a dispatch method to collect any actions it dispatches
var actions = [];
var dispatch = a => actions.push(a);
action(dispatch);
// now return the actions that the action thunk dispatched
return Rx.Observable.of(actions);
});
// pass actionStream to your stateFns instead of passing the raw subject
var state$ = stateFn(initState, actionStream);
// Now your UI code *can* pass in "smart" actions:
actionSubject.onNext(addTodo(args));
// or "dumb" actions:
actionSubject.onNext(new SomeSimpleAction(args));
请注意,上面的所有代码都在发送操作的代码中。我没有显示任何状态函数。状态函数应该是纯函数,类似于:
function todos(initState: Todo[], actions: Observable<Action>): Observable<Todo[]> {
return actions.scan((state, action) => {
if (action instanceof AddTodoAction) {
const newTodo = {id: action.todoId, text: action.text, completed: false};
return [...state, newTodo];
} else if (action instanceof EnrichTodoWithParsedData) {
// (replace the todo inside the state array with a new updated one)
} else if (action instanceof AddedToCalendar) {
// (replace the todo inside the state array with a new updated one)
}
} else {
return state;
}
}, initState);
}
函数Todo(initState:Todo[],操作:可观察):可观察{
返回操作。扫描((状态,操作)=>{
if(AddTodoAction的操作实例){
const newTodo={id:action.todoId,text:action.text,completed:false};
返回[…状态,newTodo];
}else if(EnrichTodoWithParsedData的操作实例){
//(将状态数组中的todo替换为新的更新todo)
}else if(AddedToCalendar的操作实例){
//(将状态数组中的todo替换为新的更新todo)
}
}否则{
返回状态;
}
},国家);
}
谢谢你的详细回答。一切看起来都很好。我不太喜欢的一件事是,我必须在addToDo和parseToDo中插入对possiblyUpdateCalendar
的调用(好的,对于这个简单的例子,它是有效的,但假设我有很多操作可以在Todo上添加日期,因此在每个操作中,我都必须添加可能的更新日历
).另一个问题-如果Todo是相互依赖的,该怎么办?如果我在Todo上添加日期,我必须在后端重新验证相邻Todo上的日期?关于相互依赖性的问题:在我的示例中未显示的是,redux thunks获得第二个参数(getState
),该参数允许这些所谓的“智能操作”以实际访问整个状态树。因此,您可以检查状态并应用所需的任何逻辑,以准确确定要分派的进一步操作或要执行的服务器调用。这样就可以轻松地遍历TODO并收集所有需要更新的TODO并将其发送到服务器进行处理,然后分派操作w当服务器返回到更新状态时。至于对可能的更新日历的多次调用
:目前我没有一个好的解决方案。我的项目中没有出现足够的问题来找到或设计一个干净的解决方案。
function todos(initState: Todo[], actions: Observable<Action>): Observable<Todo[]> {
return actions.scan((state, action) => {
if (action instanceof AddTodoAction) {
const newTodo = {id: action.todoId, text: action.text, completed: false};
return [...state, newTodo];
} else if (action instanceof EnrichTodoWithParsedData) {
// (replace the todo inside the state array with a new updated one)
} else if (action instanceof AddedToCalendar) {
// (replace the todo inside the state array with a new updated one)
}
} else {
return state;
}
}, initState);
}