1.3.8 • Published 4 years ago

redux-mvc v1.3.8

Weekly downloads
-
License
ISC
Repository
-
Last release
4 years ago

Redux MVC is a new way to handle the redux store of your app by removing most of the boiler plate and useless redundant code. Redux MVC removes the actions, their types, and the reducers. It also removes redundant data formatting, checking, and selecting functions treating your datas. And removes a lot of line of codes from the components and containers

How does it work ?

1. Objects are replaced with Model.

Get your code safer, cleaner by centralizing everything concerning your data at the same place. Don't create another util function to sort your message list, don't create another selectors function, don't spoil your app architecture. A new kind of object to handle ? Just create a new Model.

e.g Example of class concerning the User data

class UserModel extends Model {
    constructor(u = {id: 123, name: 'Paul', token: 'b615f293-225e-47cd-bae5-e37f94206bc7'} ){
        super(u)
    }

    getID = () => this.get().id
    getName = () => this.get().name
    getToken = () => this.get().token
	
    //use the run function to deeply update your models
    setName = (name) => this.run((state) => state.name = name)
}

export default UserModel

More about the Model class

2. Reducers are replaced with States

? - The State class is children of Model, so it has and works with all the Model methods.

You don't create anymore a function containing an unending switch case.

A State is a class initialized with a default state as you were used to do with reducer. In this State class, you can directly creates the method you need to interact with your state (write/read or setter/getter)

e.g: Example of State class concerning the Users data

class UserState extends State {

    //Must be implemented in every state class
    public new = (userState: any): UserState => new UserState(userState)

    constructor(state: any){
        super({ 
            user: new UserModel(state.user) 
        }, 'user')
    }
    
    updateName = (name) => this.run((state) => state.user.setName(name)) 
    getUser = () => this.get().user //return the UserModel
}

export default new UserState({user: {id: 123, name: 'Paul', token: 'b615f293-225e-47cd-bae5-e37f94206bc7'}})

More about the State class

3. Actions are replaced with Controllers

? - The Controller class is children of Model, so it has and works with all the Model methods. Now, You don't have to create a new type, import it in your reducer, and add another line your switch case function. You don't dispatch anymore a type and a data.

You just have to create a function checking the data, and then call the method you need from the state binded with your Controller.

e.g: Here it is.

import UserState from '../states/user'

class UserController extends Controller {
    constructor(stateClass: any){
        super(stateClass)
    }
    
    /*
    OPTION 1.
    without any middleware
    */
    updateName = (newName) => {
    	const formatedName = formatName(newName)
	if (formatedName.length < 30){
	   /*
	   This line replace dispatch.
	   It will basically dispatch the new  state once it has been updated in your State class.
	   */
	  return this.run((state) => state.updateName(formatedName))
	}
    }
    
    /* 
    OPTION 2.
    with redux thunk to potentially make many dispatch at the same time
    or making it asynchrone
    */
    updateName2 = (newName) => {
         return async (dispatch: Function) => {
            await new Promise(resolve => setTimeout(resolve, 200)) //this is a timeout
	    const formatedName = formatName(newName)
	    if (formatedName.length < 30){
	 	dispatch(this.run((state: any) => state.updateName(formatedName)))
		/*
	          //Also enable multi dispatching. 
		  dispatch(this.run((state: any) => state.something1(random1, random2)))
		  dispatch(this.run((state: any) => state.something2(random3)))
		*/
	    }
         }
     }
}

export default UserController(UserState)

More about the Controller class

1. Connect with components

on the component side almost nothing excepted the way mapStateToProps is handled.

Here is a component.

import React from 'react';
import { connect } from 'redux-mvc'
import UserController from '../redux/controllers/user'

const Main = (props) => {
const {
        user,
	updateName
    } = props

    return (
        <div>
	   <span>{user.getName()}</span>
           <button onClick={() => updateName( 'Brian' ) } type="button">Update Name</button>
        </div>
    )
}

const mapStateToProps = (state) => {
   return {
      /*
      The extend methods is native from the Controller class.
      This methods pick up in the whole redux state, the right object and then transform 
      it into the instancied State class binded with the Controller it is called from.
      so here we call it from the user Controller, the state binded with is the User State.
      So it return the User State.
      ...then we call the getUser method to get the User Model.
      */
      user: UserController.extend(state).getUser()
   }
}

/*
The list listMethods methods, native from the Controller class, just return an object with
all the methods dispatching a new state, this avoid to select them each time.
(You can manually add each method if you want to)
*/
export default connect(mapStateToProps, UserController.listMethods())(Main)

Single file get started

import React from 'react';
import ReactDOM from 'react-dom';

// import store, { history } from './redux/store';
import { 
    Provider, 
    initialize, 
    reduxMVCMiddleware, 
    applyMiddleware,
    createStore,
    Model,
    State,
    connect,
    Controller
} from 'redux-mvc';

import * as serviceWorker from './serviceWorker';


//----------------- USER MODEL ----------------- //



class UserModel extends Model {
    constructor(u = {id: 123, name: 'Paul', token: 'b615f293-225e-47cd-bae5-e37f94206bc7'} ){
        super(u)
    }

    getID = () => this.get().id
    getName = () => this.get().name
    getToken = () => this.get().token
	
    //use the run function to deeply update your models
    setName = (name: string) => this.run((state) => state.name = name)
}



//----------------- USER STATE ----------------- //



class UserState extends State {

    //Must be implemented in every state class
    public new = (userState: any): UserState => new UserState(userState)

    constructor(state: any){
        super({ 
            user: new UserModel(state.user) 
        }, 'user')
    }
    
    updateName = (name: string) => this.run((state) => state.user.setName(name)) 
    getUser = () => this.get().user //return the UserModel
}

const UserStateInstancied = new UserState({user: {id: 123, name: 'Paul', token: 'b615f293-225e-47cd-bae5-e37f94206bc7'}})



//----------------- USER CONTROLLER ----------------- //



class UserController extends Controller {
    constructor(stateClass: any){
        super(stateClass)
    }
    
    updateName = (newName: string) => this.run((state: any) => state.updateName(newName))
}

const UserControllerInstancied = new UserController(UserStateInstancied)



//----------------- COMPONENT ----------------- //



const Main = (props: any) => {
const {
    user,
	updateName
    } = props

    return (
        <div>
	   <span>{user.getName()}</span>
           <button onClick={() => updateName( 'Brian' ) } type="button">Update Name</button>
        </div>
    )
}

const mapStateToProps = (state: any) => { 
    return {
      /*
      The extend methods is native from the Controller class.
      This methods pick up in the whole redux state, the right object and then transform 
      it into the instancied State class binded with the Controller it is called from.
      so here we call it from the user Controller, the state binded with is the User State.
      So it return the User State.
      ...then we call the getUser method to get the User Model.
      */
      user: UserControllerInstancied.extend(state).getUser()
   }
}
/*
The list listMethods methods, native from the Controller class, just return an object with
all the methods dispatching a new state, this avoid to select them each time.
(You can manually add each method if you want to)
*/
const MainComponent = connect(mapStateToProps, UserControllerInstancied.listMethods(), null, {})(Main)



//----------------- STORE ----------------- //



const store = createStore(
	//this is initializing all the states.
	//-> the function takes 2 parameters the parameters of states in an array (here: states)
	//this second parameters has only been made to integrate react-router.
	initialize([UserStateInstancied.getReducerConfig()] /*, {router: connectRouter(history)}*/  ),
	
	//redux thunk works with redux-mvc
	//redux-sage doesn't work with it and redux-logger has no interest since action have been removed.
	//a logger should be made and adapted with redux mvc if needed.
	applyMiddleware(reduxMVCMiddleware /*, routerMiddleware(history) */)
)



//----------------- APP ----------------- //



const App = () => {
    return (
        <Provider store={store}>
            <MainComponent />
        </Provider>
    )
}


ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

Try this example HERE

1.3.8

4 years ago

1.3.7

4 years ago

1.3.6

4 years ago

1.3.5

4 years ago

1.3.4

4 years ago

1.3.3

4 years ago

1.3.2

4 years ago

1.3.1

4 years ago

1.3.0

4 years ago

1.2.0

4 years ago

1.1.9

4 years ago

1.1.7

4 years ago

1.2.2

4 years ago

1.2.1

4 years ago

1.1.6

4 years ago

1.1.5

4 years ago