Skip to content

Commit 5978854

Browse files
authored
Merge pull request #88 from vankeisb/feature/fix-model-undef-check
Fix model undef check, update README
2 parents f4f3f44 + 6f27077 commit 5978854

2 files changed

Lines changed: 148 additions & 146 deletions

File tree

doc/quickstart.md

Lines changed: 147 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ You'll need to setup the project before you get to actual coding.
1212
You need a React project in order to start using tea-cup.
1313
In order to keep things simple, we'll use the infamous `create-react-app` :
1414

15-
npx create-react-app my-app --typescript
15+
yarn create react-app my-app --template typescript
1616
cd my-app
1717

1818
### Add tea-cup to your dependencies
1919

20-
npm install react-tea-cup --save
20+
yarn add -D react-tea-cup tea-cup-core
2121

2222
### Run the dev server
2323

2424
We'll start the dev server, and let it recompile everything
2525
when we save files :
2626

27-
npm start
27+
yarn start
2828

2929
This should also open your default browser to http://localhost:3000
3030
and show you the default generated app.
@@ -37,7 +37,8 @@ favourite editor, and replace its contents with this :
3737
```typescript jsx
3838
import React from 'react';
3939
// bare minimum to use tea-cup
40-
import { Dispatcher, Cmd, Program, Sub } from 'react-tea-cup';
40+
import { Program } from 'react-tea-cup';
41+
import { Cmd, Dispatcher, Sub } from 'tea-cup-core';
4142

4243
// a Model can be anything. Here, it's simply a number...
4344
type Model = number;
@@ -115,185 +116,186 @@ and Cmds.
115116
Replace the contents of `App.tsx` with the following, code :
116117

117118
```typescript jsx
118-
import {Http, Cmd, Task, Result, Dispatcher, Decode, Decoder, Maybe, nothing, Sub, Program, just} from "react-tea-cup";
119+
import {Http, Cmd, Task, Result, Dispatcher, Decode, Decoder, Maybe, nothing, Sub, just} from "tea-cup-core";
120+
import {Program} from "react-tea-cup";
119121
import * as React from 'react'
120122

121123

122124
interface Model {
123-
// name of the repo
124-
readonly repo: string
125-
// use Maybe of Result to model state :
126-
// * nothing : loading commits
127-
// * just(error) : last fetch has failed, we have the error
128-
// * just(commit[]) : last fetch succeeded, we havbe the commits
129-
readonly commits: Maybe<Result<Error,ReadonlyArray<Commit>>>
125+
// name of the repo
126+
readonly repo: string
127+
// use Maybe of Result to model state :
128+
// * nothing : loading commits
129+
// * just(error) : last fetch has failed, we have the error
130+
// * just(commit[]) : last fetch succeeded, we havbe the commits
131+
readonly commits: Maybe<Result<Error,ReadonlyArray<Commit>>>
130132
}
131-
133+
132134
interface Commit {
133-
readonly sha: string
134-
readonly author: string
135+
readonly sha: string
136+
readonly author: string
135137
}
136-
137138

138-
type Msg
139-
// repo name changed
140-
= { type: "repo-changed", value: string }
141-
// trigger a fetch of commits
142-
| { type: "fetch" }
143-
// got fetch response, or an error
144-
| { type: "got-commits", commits: Result<Error,ReadonlyArray<Commit>> }
139+
140+
type Msg
141+
// repo name changed
142+
= { type: "repo-changed", value: string }
143+
// trigger a fetch of commits
144+
| { type: "fetch" }
145+
// got fetch response, or an error
146+
| { type: "got-commits", commits: Result<Error,ReadonlyArray<Commit>> }
145147

146148

147149
function init(repo: string): [Model, Cmd<Msg>] {
148-
return [
149-
{
150-
repo: repo, // store initial repo name
151-
commits: nothing // initial state is "loading"
152-
},
153-
fetchCommits(repo) // initial fetch
154-
]
150+
return [
151+
{
152+
repo: repo, // store initial repo name
153+
commits: nothing // initial state is "loading"
154+
},
155+
fetchCommits(repo) // initial fetch
156+
]
155157
}
156158

157159
function view(dispatch: Dispatcher<Msg>, model: Model) {
158-
return (
159-
<>
160-
<h1>
161-
Fetch data using Http module
162-
</h1>
163-
<input
164-
type="text"
165-
value={model.repo}
166-
onChange={e => {
167-
// update repo name on input change
168-
const value = (e.target as HTMLInputElement).value;
169-
dispatch({
170-
type: "repo-changed",
171-
value: value
172-
})
173-
}}/>
174-
<button
175-
disabled={
176-
// disable the button if we are fetching
177-
model.commits.type === "Nothing"
178-
}
179-
onClick={() =>
180-
// fetch commits on click
181-
dispatch({ type: "fetch" })
182-
}
183-
>
184-
Fetch commits
185-
</button>
186-
<hr/>
187-
{
188-
model.commits
189-
// commits are present : map the result
190-
.map(commitsResult =>
191-
// use Result.match to map the result
192-
// depending if it's ok, or an error
193-
commitsResult.match(
194-
// all good, we have commits, build the vdom
195-
commits =>
196-
<ul>
197-
{ commits.map(commit =>
198-
<li key={commit.sha}>
199-
{commit.sha} - {commit.author}
200-
</li>
201-
)}
202-
</ul>,
203-
// in case there's an error, show it
204-
error =>
205-
<p>Fetch error : {error.message}</p>
206-
)
207-
)
208-
// commits == nothing, we are currently fetching
209-
.withDefault(
210-
<p>Fetching...</p>
211-
)
212-
}
213-
</>
214-
)
160+
return (
161+
<>
162+
<h1>
163+
Fetch data using Http module
164+
</h1>
165+
<input
166+
type="text"
167+
value={model.repo}
168+
onChange={e => {
169+
// update repo name on input change
170+
const value = (e.target as HTMLInputElement).value;
171+
dispatch({
172+
type: "repo-changed",
173+
value: value
174+
})
175+
}}/>
176+
<button
177+
disabled={
178+
// disable the button if we are fetching
179+
model.commits.type === "Nothing"
180+
}
181+
onClick={() =>
182+
// fetch commits on click
183+
dispatch({ type: "fetch" })
184+
}
185+
>
186+
Fetch commits
187+
</button>
188+
<hr/>
189+
{
190+
model.commits
191+
// commits are present : map the result
192+
.map(commitsResult =>
193+
// use Result.match to map the result
194+
// depending if it's ok, or an error
195+
commitsResult.match(
196+
// all good, we have commits, build the vdom
197+
commits =>
198+
<ul>
199+
{ commits.map(commit =>
200+
<li key={commit.sha}>
201+
{commit.sha} - {commit.author}
202+
</li>
203+
)}
204+
</ul>,
205+
// in case there's an error, show it
206+
error =>
207+
<p>Fetch error : {error.message}</p>
208+
)
209+
)
210+
// commits == nothing, we are currently fetching
211+
.withDefault(
212+
<p>Fetching...</p>
213+
)
214+
}
215+
</>
216+
)
215217
}
216218

217219
function update(msg:Msg, model:Model): [Model, Cmd<Msg>] {
218-
switch (msg.type) {
219-
case "repo-changed":
220-
// just change the repo in Model
221-
return [
222-
...model, repo: msg.value },
223-
Cmd.none()
224-
]
225-
case "fetch":
226-
// indicate that we are fetching (set commits to nothing)
227-
// and trigger thje fetch
228-
return [
229-
...model, commits: nothing },
230-
fetchCommits(model.repo)
231-
]
232-
case "got-commits":
233-
// got the fetch response, assign it to
234-
// the Model
235-
return [
236-
{ ...model, commits: just(msg.commits) },
237-
Cmd.none()
238-
]
239-
}
220+
switch (msg.type) {
221+
case "repo-changed":
222+
// just change the repo in Model
223+
return [
224+
...model, repo: msg.value },
225+
Cmd.none()
226+
]
227+
case "fetch":
228+
// indicate that we are fetching (set commits to nothing)
229+
// and trigger thje fetch
230+
return [
231+
...model, commits: nothing },
232+
fetchCommits(model.repo)
233+
]
234+
case "got-commits":
235+
// got the fetch response, assign it to
236+
// the Model
237+
return [
238+
{ ...model, commits: just(msg.commits) },
239+
Cmd.none()
240+
]
241+
}
240242
}
241243

242244
// no subs in this example
243245
function subscriptions(model:Model): Sub<Msg> {
244-
return Sub.none()
246+
return Sub.none()
245247
}
246248

247249
// create and return a Cmd that fetches commits
248250
// for the passed repo
249251
function fetchCommits(repo: string): Cmd<Msg> {
250-
// create a Task that fetches the commits or fails with an Error
251-
const fetchTask: Task<Error,ReadonlyArray<Commit>> =
252-
Http.jsonBody(
253-
// create a fetch Task
254-
Http.fetch(`https://api.github.com/repos/${repo}/commits`),
255-
// decode the response with this decoder
256-
Decode.array(commitDecoder)
257-
);
258-
259-
// Msg creator function
260-
function gotCommits(r:Result<Error,ReadonlyArray<Commit>>): Msg {
261-
return {
262-
type: "got-commits",
263-
commits: r
252+
// create a Task that fetches the commits or fails with an Error
253+
const fetchTask: Task<Error,ReadonlyArray<Commit>> =
254+
Http.jsonBody(
255+
// create a fetch Task
256+
Http.fetch(`https://api.github.com/repos/${repo}/commits`),
257+
// decode the response with this decoder
258+
Decode.array(commitDecoder)
259+
);
260+
261+
// Msg creator function
262+
function gotCommits(r:Result<Error,ReadonlyArray<Commit>>): Msg {
263+
return {
264+
type: "got-commits",
265+
commits: r
266+
}
264267
}
265-
}
266-
267-
// "perform" the Task, and indicate which message
268-
// needs to be used for handling the response
269-
return Task.attempt(fetchTask,gotCommits);
268+
269+
// "perform" the Task, and indicate which message
270+
// needs to be used for handling the response
271+
return Task.attempt(fetchTask,gotCommits);
270272
}
271273

272274

273275
// create a Msg to handle the response of the fetch
274276

275277
// A decoder for Commit objects
276278
const commitDecoder: Decoder<Commit> =
277-
Decode.map2(
278-
(sha:string, author:string) => {
279-
return {
280-
sha: sha,
281-
author: author
282-
}
283-
},
284-
Decode.field("sha", Decode.str),
285-
Decode.at(["commit", "author","name"], Decode.str)
286-
);
279+
Decode.map2(
280+
(sha:string, author:string) => {
281+
return {
282+
sha: sha,
283+
author: author
284+
}
285+
},
286+
Decode.field("sha", Decode.str),
287+
Decode.at(["commit", "author","name"], Decode.str)
288+
);
287289

288290

289291
// wire the program
290292
const App = () => (
291-
<Program
292-
init={() => init("Microsoft/TypeScript")}
293-
view={view}
294-
update={update}
295-
subscriptions={subscriptions}
296-
/>
293+
<Program
294+
init={() => init("Microsoft/TypeScript")}
295+
view={view}
296+
update={update}
297+
subscriptions={subscriptions}
298+
/>
297299
);
298300

299301
export default App;

tea-cup/src/TeaCup/Program.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class Program<Model, Msg> extends Component<ProgramProps<Model, Msg>, nev
153153
}
154154

155155
render(): ReactNode {
156-
if (this.currentModel && this.bd) {
156+
if (this.currentModel !== undefined && this.bd) {
157157
return this.props.view(this.bd, this.currentModel);
158158
}
159159
return null;

0 commit comments

Comments
 (0)