Баотоу Баохуэй Редукс

внешний интерфейс React.js koa Redux

В React родительский и дочерний компоненты могут обмениваться данными через реквизиты, а для связи с родственными компонентами требуется, чтобы данные передавались родительскому компоненту, а затем передавались от родительского компонента другому дочернему компоненту. Напишите Redux на основе взаимодействия родственных компонентов.

вопрос

Это счетчик, и одним нажатием кнопки число может быть увеличено или уменьшено на единицу. Две кнопки находятся в компоненте «Счетчик», а та, которая отображает число, — в компоненте «Число».

Взаимодействие компонентов родственного компонента

  • Сначала проанализируйте это требование, нажмите кнопку, измените число, и компонент «Число» будет перерисован.
  • Его можно абстрагировать как отправку действия, изменение состояния и выполнение метода.
  • По анализу двух предыдущих шагов видно, что ядром взаимодействия компонентов является действие (action), способ выполнения (reducer) и состояние (state).
  • действие, редуктор
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export default function reducer(state = {number: 0},action) {
    switch (action.type) {
        case types.INCREMENT:
            return {
                number: state.number + 1
            };
        case types.DECREMENT:
            return {
                number: state.number - 1
            };
        default:
            return state;
    }
}
  • Хранилище — это объект, отвечающий за предоставление трех методов: getState, диспетчеризации и подписки.
const store = {
    listeners:[],   
    getState(){
        return this.state;
    },
    dispatch(action){
        this.state = reducer(this.state,action);
        this.listeners.forEach(listener=>listener());
    },
    subscribe(listener){
        this.listeners.push(listener);
        return function () {
            this.listeners = listeners.filter(item=>item!==listener);
        }
    }
};
store.dispatch({}); //初始化state
export default store;
  • Компоненты числа и счетчика
export default class Number extends React.Component{
    componentDidMount(){
        this.unsubscribe = store.subscribe(()=>this.setState({}));
    }
    componentWillUnmount(){
        this.unsubscribe();
    }
    render(){
        return (
            <div>
                {store.getState().number}
            </div>
        )
    }
}
export default class Counter extends React.Component{
    render(){
        return (
            <div>
                <button onClick={()=>store.dispatch({type:types.INCREMENT})}>+</button>
                <button onClick={()=>store.dispatch({type:types.DECREMENT})}>-</button>
            </div>
        )
    }
}

Redux

Связь между одноуровневыми компонентами реализована выше, но возможность повторного использования оставляет желать лучшего, а слушатели в хранилище не должны изменяться внешним миром.

  • createStore, это способ создать магазин в Redux.
export default function createStore(reducer) {
    let state;
    let listeners = [];
    function getState() {
        return state;
    }
    function dispatch(action) {
        state =  reducer(state,action);
        listeners.forEach(listener=>listener());
    }
    dispatch({});
    function subscribe(listener) {
        listeners.push(listener);
        return function () {
            const index = listeners.indexOf(listener);
            listeners.splice(index,1);
        }
    }
    return {
        getState,dispatch,subscribe
    }
}
  • Вызовите createStore, передайте редюсер и верните то же хранилище, что и на предыдущем шаге.
  • Три принципа в редуксе: есть только одно хранилище; состояние доступно только для чтения и может быть изменено только при запуске действия; используйте чистую модификацию функций. Мы также следуем этим принципам при написании собственного редукса.

несколько редукторов

  • Поскольку есть только один магазин, для нескольких редукторов, редукторы должны быть объединены.
export default function combineReducers(reducers) {
    return function (state = {},action) {
        let newState = {};
        for(const key in reducers){
            newState[key] = reducers[key](state[key],action);
        }
        return newState;
    }
};
  • Вызов combReducers, параметром является объект, ключом объекта может быть имя редуктора, значением является редюсер, возвращаем функцию, передаем функцию в createStore и создаем хранилище.

Упрощение диспетчеризации действий в компонентах

  • Когда мы отправляем действие, нам нужно
<button onClick={()=>store.dispatch({type:types.INCREMENT})}>+</button>
  • Это более хлопотно, если действие поставить прямо на инстанс, будет удобнее.
export default class Counter extends React.Component{
    action = bindActionCreators(actions,store.dispatch)
    render(){
        return (
            <div>
                <button onClick={this.action.increment}>+</button>
                <button onClick={this.action.decrement}>-</button>
            </div>
        )
    }
}
  • Реализуйте действия в первую очередь
export default {
    increment() {
        return {
            type: 'INCREMENT'
        }
    },
    decrement() {
        return {
            type: 'DECREMENT'
        }
    },
    changeText(value) {
        return {
            type: 'CHANGE_TEXT',
            text: value
        }
    }
}
  • Повторная реализация bindActionCreators
export default function bindActionCreators(actions,dispatch) {
    let boundActionCreators = {};
    for (const attr in actions){
        boundActionCreators[attr] = function () {
            const action = actions[attr](...arguments);
            dispatch(action);
        }
    }
    return boundActionCreators;
}

React-Redux

  • Из приведенного выше кода видно, что многие коды в компоненте повторяются, и компонент можно дополнительно абстрагировать и, наконец, абстрагировать в React-Redux.
  • В React-Redux нужно реализовать внешний компонент, отвечающий за передачу хранилища и отрисовку подкомпонентов, функция относительно простая.
export default class Provider extends Component {
    static childContextTypes = {
        store:propTypes.object
    }
    getChildContext(){
        return {
            store:this.props.store
        }
    }
    render(){
        return this.props.children
    }
}
  • Также реализуйте компонент более высокого порядка.Компонент более высокого порядка сначала возвращает функцию, а затем возвращает компонент. Компоненты более высокого порядка отвечают за передачу состояния и отправку в хранилище в качестве свойств компонентов, которые необходимо отобразить, а также за реализацию общедоступных функций в функциях жизненного цикла.
export default function (mapStateToProps,mapDispatchToProps) {
    return function (Component) {
        return class ProxyComponent extends React.Component{
            static contextTypes = {
                store:propTypes.object
            }
            constructor(props,context){
                super(props,context);
                this.store = context.store;
                this.state = mapStateToProps(this.store.getState());
            }
            componentDidMount(){
                const store = this.store;
                this.unsubscribe = store.subscribe(()=>this.setState(mapStateToProps(store.getState())));
            }
            componentWillUnmount(){
                this.unsubscribe();
            }
            render(){
                const actions  = bindActionCreators(mapDispatchToProps,this.store.dispatch);
                return <Component
                    {...actions}
                    {...this.state}
                />
            }
        }
    }
}
  • Рендеринг главной страницы
ReactDOM.render(
    <Provider store={store}>
        <Counter/>
        <Number/>
    </Provider>, document.getElementById('root'));
  • Компоненты счетчика и числа
class Counter extends React.Component{
    render(){
        return (
            <div>
                <button onClick={()=>this.props.increment()}>+</button>
                <button onClick={()=>this.props.decrement()}>-</button>
            </div>
        )
    }
}
export default connect(state=>state.counter,actions)(Counter);
class Number extends React.Component{
    render(){
        return (
            <div>
                {this.props.number}
            </div>
        )
    }
}

export default connect(state=>state.counter,actions)(Number);

избыточное промежуточное ПО

  • Наконец, реализуйте промежуточное ПО Redux. Промежуточное ПО Redux — это луковая модель, такая же, как промежуточное ПО Koa.
  • В разработке будет несколько промежуточных программ.Промежуточное ПО является функцией.Результат первого промежуточного ПО должен быть передан в качестве параметра второму промежуточному ПО, которое будет выполняться последовательно.Сначала должна быть реализована функция компоновки.
function compose(...fns) {
    if (fns.length===0) return arg=>arg;
    return fns.reduce((prev, next, index) => (...args) => prev(next(...args)));
}
export default compose;
  • Применить промежуточную функцию applyMiddleware
export default function applyMiddleware(...middlewares) {
    return function (createStore) {
        return function (reducers) {
            const store = createStore(reducers);
            let dispatch = store.dispatch;
            let middlewareApi = {
                dispatch:action=>dispatch(action),
                getState:store.getState
            };
            middlewares = middlewares.map(middleware=>middleware(middlewareApi));
            dispatch = compose(...middlewares)(dispatch);
            return {
                ...store,
                dispatch
            };
        };
    }
}
  • Используйте промежуточное ПО для изменения магазина
const store = applyMiddleware(thunk, logger)(createStore)(reducers);

Суммировать

Redux управляет состоянием страницы и передачей данных, с самого начала взаимодействия компонентов реализует библиотеку, похожую на Redux шаг за шагом, что нам удобно для изучения Redux и понимания принципа работы Redux.

Ссылаться на