redux_rs/
lib.rs

1//! # redux-rs - A Rust implementation of Redux.
2//!
3//! Redux-rs is a predictable state container for Rust applications.
4//!
5//! The goal of this project is to provide _similar_ functionality as its Javascript counterpart.
6//! However, due to the differences between Javascript and Rust, the API is not exactly the same.
7//!
8//! This project offers the following functionality:
9//! - A lock-free store, where you can dispatch actions to, with only a shared reference (`&Store`)
10//! - Flexible middleware that can intercept/modify/launch actions at any time
11//!
12//! ## Concepts
13//!
14//! Data in the redux store is immutable. The only way to update the data in the store is by dispatching actions to the store.
15//! The data is altered using a provided reducer.
16//!
17//! Middleware can be used to introduce side effects when dispatching actions.
18//! An example of a side effect is making an API call.
19//!
20//! ### State
21//!
22//! A state is the form of data that Redux manages. 
23//! Theoretically it could be anything, but as an example, let's consider a simple counter.
24//! The counter can only increment and decrement.
25//! The state would look like this:
26//!
27//! ```
28//! #[derive(Default)]
29//! struct State {
30//!     counter: i8
31//! }
32//! ```
33//!
34//! ### Actions
35//!
36//! In order to change the state, we need to dispatch actions. In Rust, the different actions would usually be represented by an enum.
37//! In the case of our counter example, we want to be able to increment and decrement the counter value.
38//!
39//! ```
40//! enum Action {
41//!     Increment,
42//!     Decrement
43//! }
44//! ```
45//!
46//! ### Reducer
47//!
48//! To actually change the state (read: create a new one), we need what is called a **reducer**.
49//! A reducer is a pure function which takes in the current state plus the action to perform and returns a new state.
50//!
51//! >Note: A reducer is a pure function: it should not introduce any side-effects.
52//!
53//! ```
54//! # #[tokio::main(flavor = "current_thread")]
55//! # async fn async_test() {
56//! # use redux_rs::Store;
57//! #
58//! # #[derive(Default)]
59//! # struct State {
60//! #     counter: i8
61//! # }
62//! #
63//! # enum Action {
64//! #     Increment,
65//! #     Decrement
66//! # }
67//! #
68//! fn reducer(state: State, action: Action) -> State {
69//!     match action {
70//!         Action::Increment => State {
71//!             counter: state.counter + 1
72//!         },
73//!         Action::Decrement => State {
74//!             counter: state.counter - 1
75//!         }
76//!     }
77//! }
78//! # let _ = Store::new(reducer);
79//! # }
80//! ```
81//!
82//! Note how the reducer uses the old data to create a new state.
83//!
84//! ### Store
85//!
86//! To put it all together, we use a store that keeps track of a state and provides an easy to use API for dispatching actions.
87//! The store takes the reducer and an initial state.
88//!
89//! ```
90//! # #[tokio::main(flavor = "current_thread")]
91//! # async fn async_test() {
92//! # use redux_rs::Store;
93//! # #[derive(Default)]
94//! # struct State {
95//! #     counter: i8
96//! # }
97//! #
98//! # enum Action {
99//! #     Increment,
100//! #     Decrement
101//! # }
102//! #
103//! # fn reducer(state: State, action: Action) -> State {
104//! #     match action {
105//! #         Action::Increment => State {
106//! #             counter: state.counter + 1
107//! #         },
108//! #         Action::Decrement => State {
109//! #             counter: state.counter - 1
110//! #         }
111//! #     }
112//! # }
113//! #
114//! // The store needs to be mutable as it will change its inner state when dispatching actions.
115//! let mut store = Store::new(reducer);
116//!
117//! // Let it do its highly complex math.
118//! store.dispatch(Action::Increment).await;
119//! store.dispatch(Action::Decrement).await;
120//!
121//! // Print the current count.
122//! println!("{}", store.select(|state: &State| state.counter).await);
123//! # };
124//! ```
125//!
126//! ### Subscriptions
127//!
128//! Sometimes one might want to listen to changes happening. This is where subscriptions come in.
129//! Subscriptions are callbacks with the current state that get called whenever an action gets dispatched.
130//!
131//! ```
132//! # #[tokio::main(flavor = "current_thread")]
133//! # async fn async_test() {
134//! # #[derive(Default)]
135//! # struct State {
136//! #     counter: i8
137//! # }
138//! #
139//! # enum Action {
140//! #     Increment,
141//! #     Decrement
142//! # }
143//! #
144//! # fn reducer(state: State, action: Action) -> State {
145//! #     match action {
146//! #         Action::Increment => State {
147//! #             counter: state.counter + 1
148//! #         },
149//! #         Action::Decrement => State {
150//! #             counter: state.counter - 1
151//! #         }
152//! #     }
153//! # }
154//! #
155//! # let mut store = redux_rs::Store::new(reducer);
156//! #
157//! store.subscribe(|state: &State| {
158//!      println!("Something changed! Current value: {}", state.counter);
159//! }).await;
160//! # }
161//! ```
162
163mod middleware;
164pub mod middlewares;
165mod reducer;
166mod selector;
167mod store;
168mod subscriber;
169
170pub use middleware::{MiddleWare, StoreApi, StoreWithMiddleware};
171pub use reducer::Reducer;
172pub use selector::Selector;
173pub use store::Store;
174pub use subscriber::Subscriber;