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