1.0.0 • Published 6 years ago

recontext v1.0.0

Weekly downloads
29
License
-
Repository
github
Last release
6 years ago

Install

npm install recontext --save

Basic usage

Create a Context

import * as React from 'react';
import * as Recontext from 'recontext';
import { init } from './actions';

type ContextState = Recontext.ContextState<{
  a: number;
  b: number;
  c: number;
  updateA: (a: number) => void;
  updateB: (b: number) => void;
}>;

const { Provider: ReactProvider, Consumer } = React.createContext<ContextState>();

class Provider extends Recontext.Provider<ContextState> {
  constructor(props) {
    super(props);

    // initialize states
    this.state = {
      contextState: {
        a: 1,
        b: 2,
        c: 3,
        updateA: this.updateA,
        updateB: this.updateB,
        dispatch: this.dispatch,
        subscribe: this.subscribe,
      }
    }
  }

  render() {
    return (
      <ReactProvider value={this.state.contextState}>
        {this.props.children}
      </ReactProvider>
    )
  }

  componentDidMount() {
    // execute initialize action
    this.dispatch(init());
  }

  // actions
  updateA = (a: number) => {
    this.setContextState(({b}) => {
      return {
        a,
        c: a + b,
      }
    })
  }

  updateB = (b: number) => {
    this.setContextState(({a}) => {
      return {
        b,
        c: a + b,
      }
    })
  }
}

function withConsumer<Props>(Component: React.ComponentType<Props>): React.ComponentType<Props & ContextState> {
  return React.forwardRef((props, ref) => (
    <Consumer>
      {
        state => <Component {...state} {...props} ref={ref}/>
      }
    </Consumer>
  ));
}

export {
  Provider,
  Consumer,
  withConsumer,
  ContextState,
}

Create Actions

// actions/updateB.ts
import { ContextState } from '../context';

export default () => ({b, updateB}: ContextState) => {
  updateB(b + 1);
}
// actions/init.ts
import { ContextState } from '../context'

export default () => ({subscribe}: ContextState) => {
  const unsubscribe: () => void = subscribe((state, prevState) => {
    if (state.a !== prevState.a) {
      console.log(`Changed a: ${prevState.a} → ${state.a}`);
    }
  });

  return () => {
    console.log('Destroy subscribed action');
    unsubscribe();
  }
}

Create Components

// components/Print.tsx
import * as React from 'react';
import { Consumer } from '../context';

export default () => {
  return (
    <Consumer>
      {
        ({a, b, c}) => (
          <div>
            {a} + {b} = {c}
          </div>
        )
      }
    </Consumer>
  );
}
// components/UpdateA.tsx
import * as React from 'react';
import { ContextState, withConsumer } from '../context';

export interface Props {
}

interface InternalProps extends ContextState {
}

interface State {
}

class Component extends React.Component<Props & InternalProps, State> {
  static displayName: string = 'UpdateA';

  render() {
    return (
      <div>
        <button onClick={this.updateA}>
          Update A
        </button>
      </div>
    );
  }

  updateA = () => {
    this.props.updateA(Math.floor(Math.random() * 10000));
  };
}

export default withConsumer<Props>(Component) as React.ComponentType<Props>;
// components/UpdateB.tsx
import * as React from 'react';
import { updateB } from '../actions';
import { ContextState, withConsumer } from '../context';

export interface Props {
}

interface InternalProps extends ContextState {
}

interface State {
}

class Component extends React.Component<Props & InternalProps, State> {
  static displayName: string = 'UpdateB';

  render() {
    return (
      <div>
        <button onClick={this.update}>
          Update B
        </button>
      </div>
    );
  }

  update = () => {
    this.props.dispatch(updateB());
  };
}

export default withConsumer<Props>(Component) as React.ComponentType<Props>;

Assemble

import * as React from 'react';
import { Print, UpdateA, UpdateB } from './components';
import { Provider } from './context';

export default () => {
  return (
    <Provider>
      <Print/>
      <UpdateA/>
      <UpdateB/>
    </Provider>
  );
}

Separate Store

Create a Store

import * as Recontext from 'recontext';

module test {
  interface State {
    x: number;
    y: number;
    z: number;
  }

  interface Actions {
    updateX: (x: number) => void;
    updateY: (y: number) => void;
  }

  export type Store = State & Actions;

  export const createStore: Recontext.CreateStore<State, Actions> = Recontext.createStore<State, Actions>(setState => ({
    updateX: ({y}) => x => {
      setState({
        x,
        z: x + y,
      });
    },
    updateY: ({x}) => y => {
      setState({
        y,
        z: x + y,
      });
    },
  }));
}

export default test;

Combine to Context

import { test } from './stores'

type ContextState = Recontext.ContextState<{
  ...
  test: test.Store;
}>;

...

class Provider extends Recontext.Provider<ContextState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      contextState: {
        ...
        test: test.createStore(this, {
          x: 10,
          y: 20,
          z: 30,
        })
      }
    }
  }
}

Create a Component

import * as React from 'react';
import { ContextState, withConsumer } from '../context';

export interface Props {
}

interface InternalProps extends ContextState {
}

interface State {
}

class Component extends React.Component<Props & InternalProps, State> {
  static displayName: string = 'UpdateX';

  render() {
    return (
      <div>
        <button onClick={this.update}>
          Update X
        </button>
      </div>
    );
  }

  update = () => {
    this.props.test.updateX(Math.floor(Math.random() * 10000));
  };
}

export default withConsumer<Props>(Component) as React.ComponentType<Props>;
1.0.0

6 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.0

6 years ago