Redux ~ Clone - rohit120582sharma/Documentation GitHub Wiki

Library

Redux Library

/**
 * Combine Reducers
 * Creates a team of sub-reducers
 * 
 */
function combineReducers(reducers) {
    const rootReducer = (state = {}, action) => {
        const newState = {};
        for(let key in reducers) {
            const subState = state[key];
            const reducerFn = reducers[key];
            newState[key] = reducerFn(subState, action);
        }
        return newState;
    }
    return rootReducer;
}

/**
 * Store Library - it should have four parts
 * 1. The state (state-tree)
 * 2. Get the state (getState)
 * 3. Listen to changes on the state (subscribe)
 * 4. Update the state (dispatch)
 * 
 */
function createStore(reducer) {
    let state;
    let listeners = [];

    const getState = () => state;

    const subscribe = (listener) => {
        listeners.push(listener);
        return () => {
            listeners = listeners.filter((l) => l !== listener);
        };
    }

    const dispatch = (action) => {
        // Call root reducer
        state = reducer(state, action);

        // Call subscribers
        listeners.forEach((listener) => listener());
    }

    dispatch({});

    return {
        getState,
        subscribe,
        dispatch,
    };
}

React-Redux Library

/** 
 * connect:
 * Render any component, passing that component any data it needs, from the store.
 */
const Context = React.createContext();

class Provider extends React.Component {
    render() {
        return (
            <Context.Provider value={this.props.store}>
                {this.props.children}
            </Context.Provider>
        );
    }
}

function connect(mapStateToProps) {
    return (BaseComponent) => {
        class Receiver extends React.Component {
            componentDidMount() {
                const { subscribe } = this.props.store;
                this.unsubscribe = subscribe(() => {
                    this.forceUpdate();
                });
            }
            componentWillUnmount() {
                this.unsubscribe();
            }
            render() {
                const { dispatch, getState } = this.props.store;
                const state = getState();
                const stateNeeded = mapStateToProps(state);
                return (
                    <BaseComponent {...stateNeeded} dispatch={dispatch} />
                );
            }
        }
        class ConnectedComponent extends React.Component {
            render() {
                return (
                    <Context.Consumer>
                        {(store) => <Receiver store={store} />}
                    </Context.Consumer>
                );
            }
        }
        return ConnectedComponent;
    }
}


Application

Redux code

/**
 * Actions
 */
const ADD_TODO = 'ADD_TODO';
const REMOVE_TODO = 'REMOVE_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';

const addTodoAction = (newTodo) => {
    return {
        type: ADD_TODO,
        payload: newTodo
    };
}
const removeTodoAction = (id) => {
    return {
        type: REMOVE_TODO,
        payload: id
    };
}
const toggleTodoAction = (id) => {
    return {
        type: TOGGLE_TODO,
        payload: id
    };
}

/**
 * Reducer (Pure Function)
 */
function todoReducer(state = [], action) {
    switch(action.type) {
        case ADD_TODO :
            return state.concat([action.payload]);
        case REMOVE_TODO :
            return state.filter((todo) => todo.id !== action.payload);
        case TOGGLE_TODO :
            return state.map((todo) => {
                return (todo.id !== action.payload)
                    ? todo
                    : Object.assign({}, todo, { complete: !todo.complete });
            });
        default :
            return state;
    }
}

/**
 * Middlewares
 */
const logger = (store) => (next) => (action) => {
    const result = next(action);
    console.group(action.type);
    console.log('The action: ', action);
    console.log('The new state: ', store.getState());
    console.groupEnd();
    return result;
}
// Thunk
//  Encapsulate async logic into the action creators
//  The action creators have to return a function that accepts dispatch as its argument
const thunk = (store) => (next) => (action) => {
    if (typeof action === 'function') {
        return action(store.dispatch);
    }
    return next(action);
}

/**
 * Store
 */
const rootReducer = Redux.combineReducers({
    todos: todoReducer
});
const store = Redux.createStore(rootReducer, Redux.applyMiddleware(logger, thunk));

React code

/**
 * Components
 */
function List({items, removeItem, toggleItem}) {
    return (
        <ul>
            {
                items.map((item) => (
                    <li key={item.id}>
                        <span
                            style={{
                                marginRight: '10px',
                                textDecoration: item.complete ? 'line-through' : 'none'
                            }}
                            onClick={() => toggleItem && toggleItem(item.id)}>
                            {item.name}
                        </span>
                        <button onClick={() => removeItem(item.id)}>X</button>
                    </li>
                ))
            }
        </ul>
    );
}

class Todos extends React.Component {
    addItem = (e) => {
        e.preventDefault();

        const name = this.input.value;
        this.input.value = '';

        this.props.dispatch(addTodoAction({
            id: generateId(),
            name,
            complete: false
        }));
    }
    removeItem = (id) => {
        this.props.dispatch(removeTodoAction(id));
    }
    toggleItem = (id) => {
        this.props.dispatch(toggleTodoAction(id));
    }

    render() {
        const { todos } = this.props;

        return (
            <div>
                <h1>Todos List</h1>
                <input
                    type='text'
                    placeholder='Add a todo'
                    ref={(input) => this.input = input}
                />
                <button onClick={this.addItem}>Submit</button>
                <List
                    items={todos}
                    removeItem={this.removeItem}
                    toggleItem={this.toggleItem} />
            </div>
        );
    }
}
const mapStateToProps = (state) => ({
    todos: state.todos
});
const ConnectedTodos = connect(mapStateToProps)(Todos);


/**
 * Render Components
 */
class App extends React.Component {
    render() {
        return (
            <React.Fragment>
                <ConnectedTodos />
            </React.Fragment>
        );
    }
}

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('app')
);
⚠️ **GitHub.com Fallback** ⚠️