redux中的应用程序与本地状态

时间:2016-01-27 17:26:22

标签: javascript reactjs redux

在许多Redux示例中,SOME_ASYNC_ACTION_ERRORSOME_ASYNC_PENDING是分派用于操纵全局状态的操作。我想不出这样一种场景,即最初使用全局错误/加载/挂起状态呈现组件是有意义的。当组件被销毁并重新安装时,需要“清除”该异步错误,使其看起来像是操纵组件的本地状态是更好的选择。

考虑到这一点,在Redux中处理加载/错误/挂起状态的最佳做法是什么:

  • 组件是否应在本地默认为初始状态,但是仍然订阅加载/错误的全局应用程序状态?
  • OR是否应在离开组件后重置错误/加载的应用程序状态?
  • 或者这些暂时状态是否应该在本地管理?

1 个答案:

答案 0 :(得分:4)

根据我的理解,Redux中的最佳做法是始终将应用程序存储在全局存储中,然后让您的各个组件使用main仅订阅该存储中的相关信息。因此,您的个人组件不会拥有全局应用程序#include <iostream> #include <vector> #include <string> #include <algorithm> #include <stdexcept> #include <type_traits> #include <boost/lexical_cast.hpp> #include <boost/any.hpp> template<typename T, bool Invert = false, typename U = void> using OptionHasValue = std::enable_if_t<(!std::is_same<T, void>::value) ^ Invert, U>; //only make this template substitution successful, if (when 'Invert' is false) T is not if type 'void' template<typename T, typename Enable = void> class OptionValue; template<typename T> class OptionValue<T, OptionHasValue<T>> //using SFINAE ("substitution failure is not an error") here { protected: T value; public: void setValue(T newValue) { value = newValue; } void setValueFromString(std::string newValueStr) { setValue(boost::lexical_cast<T>(newValueStr)); } T getValue() { return value; } bool hasValue() { return true; //if this class variant is taken by the compiler, the 'Option' that will inherit from it will have a value } }; template<typename T> class OptionValue<T, OptionHasValue<T, true>> //the opposite condition (the 'true' inverts it) { //option value is disabled, but to check if a value is available in the derived class, add a function for that (or should I not?) public: bool hasValue() { return false; } }; template<typename T> class Option : public OptionValue<T> { private: std::string identifier; std::vector<std::string> variants; public: Option(std::string newIdentifier, std::vector<std::string> newVariants) { identifier = newIdentifier; variants = newVariants; } bool hasVariant(std::string v) { return (std::find(variants.begin(), variants.end(), v) != variants.end()); } std::string getIdentifier() { return identifier; } }; class OptionSet { private: std::vector<boost::any> options; //boost::any can't be the right way to do this, or is it? std::vector<std::string> argvVec; template<typename T> Option<T>& findOptionByIdentifier(std::string identifier) { for(auto& o : options) if(o.getIdentifier() == identifier) //of course this doesn't compile, because 'o' will always be of type 'boost::any', but what should I do instead? return o; throw std::runtime_error("error: unable to find option by identifier \"" + identifier + "\"\n"); } template<typename T> Option<T>& findOptionByVariant(std::string variant) { for(auto& o : options) if(o.hasVariant(variant)) //probably almost the same compile error like in 'findOptionByIdentifier' return o; throw std::runtime_error("error: unable to find option by variant \"" + variant + "\"\n"); } public: template<typename t> void add(Option<T> opt) { options.push_back(opt); //is this the right way to add instances of classes with different template parameters to a vector? } void setArgvVec(std::vector<std::string> newArgvVec) { argvVec = newArgvVec; } void process() { for(size_t i=0; i<argvVec.size(); i++) { Option<T>& opt = findOptionByVariant(argvVec[i]); //of course this doesn't compile either, but what should I do instead? if(opt.hasValue()) { if(i == argvVec.size()-1) throw std::runtime_error("error: no value given for option \"" + argvVec[i] + "\"\n"); opt.setValueFromString(argvVec[i]); //boost::bad_lexical_cast should be caught here, but that's not important right now i++; } } } template<typename T> T getOptionValue(std::string identifier) { Option<T>& opt = findOptionByIdentifier(identifier); //a bit like the call to 'findOptionByVariant' in 'process()'. also, this variable does not have to be a reference if(!opt.hasValue()) throw std::runtime_error("error: option with identifier \"" + identifier + "\" has no value\n"); return opt.getValue(); } }; int main() { OptionSet optionSet; //it's not guaranteed that OptionSet::add will always receive a rvalue, I just do it here for shorter code/simplicity optionSet.add(Option<void>("help", { "-?", "--help" })); //if it's a void-option, the 'Option' does not have a value, if the template parameter is anything else, it has one (like below) optionSet.add(Option<std::string>("message", { "-m", "--message" })); optionSet.add(Option<int>("number", { "-n", "--number" })); optionSet.add(Option<double>("pi", { "-p", "--pi" })); optionSet.setArgvVec({ "--help", "-m", "hello", "--number", "100", "--pi", "3.14" }); optionSet.process(); std::string message = optionSet.getOptionValue("message"); int number = optionSet.getOptionValue("number"); double pi = optionSet.getOptionValue("pi"); std::cout << "Message: " << message << "\n"; //should output 'hello' std::cout << "Number: " << number << "\n"; //should output '100' std::cout << "Pi: " << pi << "\n"; //should output something like '3.140000' return 0; } 属性,而是会订阅相关标记,例如connect(mapStateToProps)(Component)

有关详情,请参阅http://rackt.org/redux/docs/basics/UsageWithReact.html

编辑:为了进一步回答你的问题,每个行动应该通过发送另一个行动来自行清理。所以你可能会loading添加一个加载标志并重置一个错误状态,然后是users.loading,它会删除加载标志,或REQUEST_USER,它会删除加载标志,但会添加一个错误状态。这种模式在这里进行了详尽的描述:https://github.com/agraboso/redux-api-middleware#redux-standard-api-calling-actions