1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
//! This library is inspired by [redux](https://redux.js.org/), and //! designed to be used within Rust GUI applications to manage //! centralised global state which behaves in a predictable way. //! //! ## Example //! //! The following is a simple example of how to use this system to //! manage state, and subscribe to changes to state. //! //! ``` //! use reactive_state::{StoreEvent, ReducerFn, ReducerResult, Store, Callback}; //! use std::{cell::RefCell, rc::Rc}; //! //! /// Something to hold the application state. //! #[derive(Clone)] //! struct MyState { //! pub variable: u32 //! } //! //! /// Some actions to perform which alter the state. //! enum MyAction { //! Increment, //! Decrement //! } //! //! /// Some events that are fired during certain action/state combinations. //! #[derive(Clone, Eq, PartialEq, Hash)] //! enum MyEvent { //! IsOne, //! None //! } //! //! /// Ensure that the event is compatible with Store. //! impl StoreEvent for MyEvent { //! fn none() -> Self { //! Self::None //! } //! fn is_none(&self) -> bool { //! if let Self::None = self { //! true //! } else { //! false //! } //! } //! } //! //! /// A reducer to perform the actions, alter the state, and fire off events. //! let reducer: ReducerFn<MyState, MyAction, MyEvent, ()> = |state, action| { //! let mut events: Vec<MyEvent> = Vec::new(); //! //! let new_state = match action { //! MyAction::Increment => { //! let mut new_state = MyState::clone(state); //! new_state.variable = state.variable + 1; //! Rc::new(new_state) //! } //! MyAction::Decrement => { //! let mut new_state = MyState::clone(state); //! new_state.variable = state.variable - 1; //! Rc::new(new_state) //! } //! }; //! //! if new_state.variable == 1 { //! events.push(MyEvent::IsOne); //! } //! //! ReducerResult { //! state: new_state, //! events, //! effects: vec![], //! } //! }; //! //! // Set the initial state. //! let initial_state = MyState { //! variable: 0u32 //! }; //! //! // Create the store. //! let store = Store::new(reducer, initial_state); //! //! // A test variable that will be altered by the callback. //! let callback_invokes: Rc<RefCell<u32>> = Rc::new(RefCell::new(0u32)); //! let callback_invokes_local = callback_invokes.clone(); //! //! let callback = Callback::new(move |_state: Rc<MyState>, _event: MyEvent| { //! *(callback_invokes_local.borrow_mut()) += 1; //! }); //! //! // Subscribe to state changes which produce the IsOne event. //! store.subscribe_event(&callback, MyEvent::IsOne); //! //! assert_eq!(0, store.state().variable); //! assert_eq!(0, *RefCell::borrow(&callback_invokes)); //! //! // Dispatch an increment action onto the store, which will //! // alter the state. //! store.dispatch(MyAction::Increment); //! //! // The state has been altered. //! assert_eq!(1, store.state().variable); //! //! // The callback was invoked. //! assert_eq!(1, *RefCell::borrow(&callback_invokes)); //! //! store.dispatch(MyAction::Increment); //! //! // The state has been altered. //! assert_eq!(2, store.state().variable); //! //! // The callback was not invoked, because the event IsOne //! // was not fired by the reducer. //! assert_eq!(1, *RefCell::borrow(&callback_invokes)); //! //! // Drop the callback, and it will also be removed from the store. //! drop(callback); //! //! store.dispatch(MyAction::Decrement); //! //! // The state has been altered again. //! assert_eq!(1, store.state().variable); //! //! // The callback was dropped before the action was dispatched, //! // and so it was not invoked. //! assert_eq!(1, *RefCell::borrow(&callback_invokes)); //! ``` //! //! ## Side Effects //! //! Something that wasn't covered in the example above, was the //! concept of side effects produced in the reducer. This is the //! fourth type parameter `Effect` on [Store](Store), and effects //! which are produced in the reducer are given to the store via the //! [ReducerResult](ReducerResult) that it returns. Side effects are //! designed to be executed/handled by store [middleware](middleware). //! //! ## Optional Features //! //! The following optional crate features can be enabled: //! //! + `"simple_logger"` - Logging middleware in the //! [simple_logger](crate::middleware::simple_logger) module which //! uses the `log` macros. //! + `"web_logger"` - Logging middleware in the //! [web_logger](crate::middleware::web_logger) module, for //! applications running in the browser using //! [wasm-bindgen](https://crates.io/crates/wasm-bindgen). //! + `"yew"` - Support for compatibility trait implementations on //! [yew](https://crates.io/crates/yew) types. #![cfg_attr(docsrs, feature(doc_cfg))] mod event; mod listener; pub mod middleware; mod reducer; mod store; #[cfg(feature = "yew")] #[cfg_attr(docsrs, doc(cfg(feature = "yew")))] pub mod provider; pub use event::*; pub use listener::*; pub use reducer::*; pub use store::{Store, StoreRef};