Skip to content
This repository has been archived by the owner on Jun 28, 2022. It is now read-only.

Commit

Permalink
#2: Add CONTAINERS.md
Browse files Browse the repository at this point in the history
  • Loading branch information
lapanti committed Apr 12, 2017
1 parent 50389dc commit f61f797
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 20 deletions.
3 changes: 3 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
* [Views](/docs/VIEWS.md)
* [Begin](/docs/VIEWS.md#begin)
* [Alternatives](/docs/VIEWS.md#alternatives)
* [Containers](/docs/CONTAINERS.md)
* [Initialize](/docs/CONTAINERS.md#initialize)
* [Begin](/docs/CONTAINERS.md#begin)
* [Styles](/docs/STYLES.md)
* [Tests](/docs/TESTS.md)
10 changes: 5 additions & 5 deletions docs/COMPONENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ We will start with the simplest component that utilizes all the tools we need fo
import * as React from 'react';

interface IButtonProps {
click();
click(): void;
readonly text: string;
}

Expand All @@ -72,11 +72,11 @@ we import all the functionalities provided by **React** under the name `React` (
On the third to sixth rows
```typescript
interface IButtonProps {
click();
click(): void;
readonly text: string;
}
```
we define an [interface](https://www.typescriptlang.org/docs/handbook/interfaces.html) to denote the [properties](https://facebook.github.io/react/docs/components-and-props.html) (*or props for short*) our **component** accepts (*and in this case needs*). `click()` defines a function, which will be known as `click` inside our `Button` and as we have only defined that it takes no arguments, we also tell the compiler that we don't care if it returns anything, just that it can be called. `readonly text: string` on the other hand defines a [readonly](https://basarat.gitbooks.io/typescript/docs/types/readonly.html) (*an immutable property*) called `text` inside our `Button`, that is of the type [string](https://www.typescriptlang.org/docs/handbook/basic-types.html).
we define an [interface](https://www.typescriptlang.org/docs/handbook/interfaces.html) to denote the [properties](https://facebook.github.io/react/docs/components-and-props.html) (*or props for short*) our **component** accepts (*and in this case needs*). `click()` defines a function, which will be known as `click` inside our `Button` and as we have only defined that it takes no arguments, we also tell the compiler that we don't care if it returns anything, just that it can be called, using the return type of `void`. `readonly text: string` on the other hand defines a [readonly](https://basarat.gitbooks.io/typescript/docs/types/readonly.html) (*an immutable property*) called `text` inside our `Button`, that is of the type [string](https://www.typescriptlang.org/docs/handbook/basic-types.html).
> **Interfaces** are shapes we define, meaning that we do not care what the actual implementation is, just that it has those types of values with those names. They cannot be instantiated as such and thus cannot contain default values like [classes](https://www.typescriptlang.org/docs/handbook/classes.html).
---
Expand Down Expand Up @@ -107,7 +107,7 @@ import Todo from '../common/Todo';

export interface ITodoComponent {
readonly todo: Todo;
setDone(i: number);
setDone(i: number): void;
}

const TodoComponent: React.StatelessComponent<ITodoComponent> = ({ todo, setDone }) => (
Expand All @@ -131,7 +131,7 @@ import Todo from '../common/Todo';
```
which imports our `Todo`-class using a relative path (*the compiler will look for the file relative to the current file*) and the second property in our `interface`
```typescript
setDone(i: number);
setDone(i: number): void;
```
which defines a function called `setDone` that takes one argument, which is a `number`.

Expand Down
87 changes: 87 additions & 0 deletions docs/CONTAINERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Containers

Next up we want to bind our [Views](/VIEWS.md) to our [Reducers](/REDUCERS.md), a.k.a. define how we get the **props** from our [state](http://redux.js.org/docs/basics/Reducers.html).

### <a name="initialize">Initialize</a>

First up we need to add a new dependency, [react-redux](https://github.com/reactjs/react-redux) to connect our **React** views to our **state**
```
yarn add react-redux
```
and the type definitions for it
```
yarn add -D @types/react-redux
```

### <a name="begin">Begin</a>

We begin by creating a file called `IndexContainer.ts` inside our `index`-folder inside `src/modules`
> All containers will have the same "prefix" as their accompanied **views**, a.k.a. `[Pagename]Container.ts`
```typescript
import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
import { State, Actions } from '../../redux/reducer';
import { setTitle, saveTodo, setDone } from './IndexReducer';
import IndexView, { IIndexState, IIndexDispatch, IIndexProps } from './IndexView';

const stateToProps = (state: State): IIndexState => ({
title: state.index.title,
todos: state.index.todos,
loading: state.index.loading,
});

const dispatchToProps = (dispatch: Dispatch<Actions>): IIndexDispatch => ({
setTitle: bindActionCreators(setTitle, dispatch),
saveTodo: bindActionCreators(saveTodo, dispatch),
setDone: bindActionCreators(setDone, dispatch),
});

export default connect<IIndexState, IIndexDispatch, IIndexProps>(stateToProps, dispatchToProps)(IndexView);
```

---

First we define a function to transform our [`State`](/REDUX.md#reducer) into the `IIndexState` required by our `IndexView`
```typescript
import { State } from '../../redux/reducer';
import { IIndexState } from './IndexView';

const stateToProps = (state: State): IIndexState => ({
title: state.index.title,
todos: state.index.todos,
loading: state.index.loading,
});
```
where we get the required information from the `State` and return it with the correct key.

---

Next up we define a function to get the correct functions require by our `IndexView` e.g. `IIndexDispatch`
```typescript
import { bindActionCreators, Dispatch } from 'redux';
import { Actions } from '../../redux/reducer';
import { setTitle, saveTodo, setDone } from './IndexReducer';
import { IIndexDispatch } from './IndexView';

const dispatchToProps = (dispatch: Dispatch<Actions>): IIndexDispatch => ({
setTitle: bindActionCreators(setTitle, dispatch),
saveTodo: bindActionCreators(saveTodo, dispatch),
setDone: bindActionCreators(setDone, dispatch),
});
```
where use the function [`bindActionCreator`](http://redux.js.org/docs/api/bindActionCreators.html) which allows us to simply call the bound function to create an `Action` and `dispatch` them to the `store`. Defining `dispatch` as type of `Dispatch<Actions>` allows us to specify that our `dispatch`-function will only dispatch actions of type `Actions` (*a.k.a. our own actions*).

---

Finally we bind it all using **react-redux's** [`connect`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options)
```typescript
import { connect } from 'react-redux';
import IndexView, { IIndexState, IIndexDispatch, IIndexProps } from './IndexView';

const stateToProps = ...
const dispatchToProps = ...

export default connect<IIndexState, IIndexDispatch, IIndexProps>(stateToProps, dispatchToProps)(IndexView);
```
where the first type argument is the type for the format we want to transform our **state** to (*in this case our `IIndexState`*), the second type argument is the format we want to transform the `dispatch`-function to (*in this case our `IIndexDispatch`*) and the last type argument is the type for the **props** our **view** expects. The first argument `connect` takes is a function to transform our **state** into the type of the first type argument and the second argument is a function to transform the `dispatch`-function into the second type argument (*here we also see for the first time a [curried function](https://en.wikipedia.org/wiki/Currying)*). The last argument is our **view** itself, which should be expecting **props** of the type of the third type argument.
15 changes: 8 additions & 7 deletions docs/VIEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Now we are ready to start working towards the beef of the application: different
### <a name="begin">Begin</a>

We will begin by creating a file called `IndexView.tsx` (*remember that 'x' in the end of the file type means that it contains [jsx](https://facebook.github.io/react/docs/jsx-in-depth.html)*) inside a folder called `index` inside the `components`-folder:
> All of our pages will be inside folders named after the view, in this case **Index**
> All of our pages will be inside folders named after the page, in this case **Index** and the view will be named `[Pagename]View.tsx`
```typescript
import * as React from 'react';
Expand All @@ -21,9 +21,9 @@ interface IIndexState {
}

interface IIndexDispatch {
setTitle(n: string);
saveTodo();
setDone(i: number);
setTitle(n: string): void;
saveTodo(): void;
setDone(i: number): void;
}

type IIndexProps = IIndexState & IIndexDispatch;
Expand Down Expand Up @@ -72,12 +72,13 @@ is an `interface` that holds the "state"-values for our `View`, e.g. the stuff t
The second `interface` we declare, called `IIndexDispatch`
```typescript
interface IIndexDispatch {
setTitle(n: string);
saveTodo();
setDone(i: number);
setTitle(n: string): void;
saveTodo(): void;
setDone(i: number): void;
}
```
is an `interface` that holds all the "dispatch"-functions for our `View`, e.g. all the functionality that our users can trigger, in this case a function (`setTitle(n: string)`) to change the current `title` (*that takes a string as an argument*), a function (`saveTodo()`) to save the new `Todo` and a function (`setDone(i: number)`) to set an existing `Todo` as done (*that takes the number of the `Todo` as argument*).
> Having [`void`](https://www.typescriptlang.org/docs/handbook/basic-types.html) as a return type allows us to not care about the return type (*which we don't need here*) without having an implicit any
---

Expand Down
2 changes: 1 addition & 1 deletion src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';

export interface IButtonProps {
click();
click(): void;
readonly text: string;
}

Expand Down
8 changes: 4 additions & 4 deletions src/modules/index/IndexContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
import { State, Actions } from '../../redux/reducer';
import { setTitle, saveTodo, setDone } from './IndexReducer';
import IndexView, { IIndexProps } from './IndexView';
import IndexView, { IIndexState, IIndexDispatch, IIndexProps } from './IndexView';

const stateToProps = (state: State) => ({
const stateToProps = (state: State): IIndexState => ({
title: state.index.title,
todos: state.index.todos,
loading: state.index.loading,
});

const dispatchToProps = (dispatch: Dispatch<Actions>) => ({
const dispatchToProps = (dispatch: Dispatch<Actions>): IIndexDispatch => ({
setTitle: bindActionCreators(setTitle, dispatch),
saveTodo: bindActionCreators(saveTodo, dispatch),
setDone: bindActionCreators(setDone, dispatch),
});

export default connect<{}, {}, IIndexProps>(stateToProps, dispatchToProps)(IndexView);
export default connect<IIndexState, IIndexDispatch, IIndexProps>(stateToProps, dispatchToProps)(IndexView);
6 changes: 3 additions & 3 deletions src/modules/index/IndexView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export interface IIndexState {
}

export interface IIndexDispatch {
setTitle(n: string);
saveTodo();
setDone(i: number);
setTitle(n: string): void;
saveTodo(): void;
setDone(i: number): void;
}

export type IIndexProps = IIndexState & IIndexDispatch;
Expand Down

0 comments on commit f61f797

Please sign in to comment.