mirror of
https://github.com/microsoft/frontend-bootcamp.git
synced 2026-01-26 14:56:42 +08:00
Merge branch 'master' of https://github.com/Microsoft/frontend-bootcamp
This commit is contained in:
@@ -7,6 +7,11 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body.ms-Fabric {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#app {
|
||||
flex: 1 1 40%;
|
||||
padding: 50px;
|
||||
@@ -41,7 +46,7 @@ body {
|
||||
}
|
||||
|
||||
#markdownReadme code {
|
||||
font-family: Consolas,Menlo,Monaco,monospace;
|
||||
font-family: Consolas, Menlo, Monaco, monospace;
|
||||
font-size: 0.9em;
|
||||
background-color: white;
|
||||
padding: 0.2em 0.4em;
|
||||
|
||||
@@ -119,7 +119,7 @@ button.onclick = displayMatches();
|
||||
You can also combine these together like this:
|
||||
|
||||
```js
|
||||
docment.querySelector('.submit').onclick = displayMatches;
|
||||
document.querySelector('.submit').onclick = displayMatches;
|
||||
```
|
||||
|
||||
Wire this up and see you function in action!
|
||||
|
||||
@@ -35,11 +35,11 @@ A view is a React component that consumes the store as its data.
|
||||
|
||||
The [store](https://redux.js.org/basics/store) consists of a **state tree**, a **dispatcher**, and **reducers**.
|
||||
|
||||
1. The **state tree** is a _singleton_, _serializable_, _immutable_ json data. It is updated from one snapshot to another through `reducers`.
|
||||
1. The **state tree** is a _singleton_, _serializable_, _immutable_ nested json data. It is updated from one snapshot to another through `reducers`.
|
||||
|
||||
2. The **dispatcher** accepts actions passing them to the reducers.
|
||||
|
||||
3. **Reducers** are functions that take in the current state tree and an action, producing the next snapshot of the state tree.
|
||||
3. **Reducers** are functions that take in the current state tree and an action, producing the next snapshot of the state tree. This is the only way to update the state tree.
|
||||
|
||||
## Why Use Redux?
|
||||
|
||||
@@ -52,17 +52,15 @@ There are lots of alternatives available, but here are some really good reasons
|
||||
|
||||
# Creating the Redux store
|
||||
|
||||
The [`createStore()`](https://redux.js.org/api/createstore) function is provided by Redux and can take in several arguments. The simplest form just takes in reducers.
|
||||
The [`createStore()`](https://redux.js.org/api/createstore) function is provided by Redux to create a store. In general, an application would just have one single store. It takes in the reducer and an initial snapshot of the state tree.
|
||||
|
||||
```ts
|
||||
const store = createStore(reducer, initialState);
|
||||
```
|
||||
|
||||
`createStore()` creates a store with a reducer, and some initial state.
|
||||
|
||||
# Writing Reducers
|
||||
|
||||
We will write our reducers with the help of some utilities from `redux-starter-kit`. Here is how we will write our reducers:
|
||||
We will write our reducers with the help of some utilities from the official `redux-starter-kit`. Here is how we will write our reducers:
|
||||
|
||||
## 1. Organize reducers according to the keys of the state tree object:
|
||||
|
||||
|
||||
@@ -5,7 +5,18 @@
|
||||
</head>
|
||||
<body class="ms-Fabric">
|
||||
<div id="markdownReadme" class="exercise" data-src="./README.md"></div>
|
||||
<div id="app"></div>
|
||||
<div id="app">
|
||||
<p>
|
||||
Nothing to show here; just look at your console window for output. Hit F12 (<code>cmd+option+I</code> on Mac) to open console
|
||||
window.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To inspect Redux store, use the <a href="http://extension.remotedev.io/">Redux Dev Tool</a> extension for your browser:
|
||||
<a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd">Chrome</a>,
|
||||
<a href="https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/">FireFox</a>. (Sorry, no Edge or IE support)
|
||||
</p>
|
||||
</div>
|
||||
<script src="../../assets/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -68,7 +68,7 @@ class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
|
||||
}
|
||||
|
||||
const ConnectedTodoHeader = connect(
|
||||
state => {},
|
||||
state => ({}),
|
||||
dispatch => ({
|
||||
addTodo: label => dispatch(actions.addTodo(label)),
|
||||
setFilter: filter => dispatch(actions.setFilter(filter))
|
||||
|
||||
@@ -24,9 +24,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const dispatch = this.context.dispatch;
|
||||
const { id, todos, complete, remove } = this.props;
|
||||
|
||||
const item = todos[id];
|
||||
|
||||
@@ -34,10 +32,10 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
<Stack horizontal verticalAlign="center" horizontalAlign="space-between">
|
||||
{!this.state.editing && (
|
||||
<>
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => dispatch(actions.complete(id))} />
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => complete(id)} />
|
||||
<div>
|
||||
<IconButton iconProps={{ iconName: 'Edit' }} onClick={this.onEdit} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => dispatch(actions.remove(id))} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => remove(id)} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -57,8 +55,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
private onEdit = () => {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const { id, todos } = this.props;
|
||||
const { label } = todos[id];
|
||||
|
||||
this.setState({
|
||||
@@ -83,9 +80,9 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
const ConnectedTodoListItem = connect(
|
||||
(state: Store) => ({ todos: state.todos }),
|
||||
dispatch => ({
|
||||
complete: label => dispatch(actions.addTodo(label)),
|
||||
remove: label => dispatch(actions.addTodo(label)),
|
||||
edit: filter => dispatch(actions.setFilter(filter))
|
||||
complete: id => dispatch(actions.complete(id)),
|
||||
remove: id => dispatch(actions.remove(id)),
|
||||
edit: (id, label) => dispatch(actions.edit(id, label))
|
||||
})
|
||||
)(TodoListItem);
|
||||
|
||||
|
||||
@@ -24,9 +24,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const dispatch = this.context.dispatch;
|
||||
const { id, todos, complete, remove } = this.props;
|
||||
|
||||
const item = todos[id];
|
||||
|
||||
@@ -34,10 +32,10 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
<Stack horizontal verticalAlign="center" horizontalAlign="space-between">
|
||||
{!this.state.editing && (
|
||||
<>
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => dispatch(actions.complete(id))} />
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => complete(id)} />
|
||||
<div>
|
||||
<IconButton iconProps={{ iconName: 'Edit' }} onClick={this.onEdit} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => dispatch(actions.remove(id))} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => remove(id)} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -57,8 +55,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
private onEdit = () => {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const { id, todos } = this.props;
|
||||
const { label } = todos[id];
|
||||
|
||||
this.setState({
|
||||
@@ -83,9 +80,9 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
const ConnectedTodoListItem = connect(
|
||||
(state: Store) => ({ todos: state.todos }),
|
||||
dispatch => ({
|
||||
complete: label => dispatch(actions.addTodo(label)),
|
||||
remove: label => dispatch(actions.addTodo(label)),
|
||||
edit: filter => dispatch(actions.setFilter(filter))
|
||||
complete: id => dispatch(actions.complete(id)),
|
||||
remove: id => dispatch(actions.remove(id)),
|
||||
edit: (id, label) => dispatch(actions.edit(id, label))
|
||||
})
|
||||
)(TodoListItem);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export const actionsWithService = {
|
||||
|
||||
edit: (id: string, label: string) => {
|
||||
return async (dispatch: any, getState: () => Store) => {
|
||||
dispatch(actions.complete(id));
|
||||
dispatch(actions.edit(id, label));
|
||||
await service.update(id, getState().todos[id]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
|
||||
}
|
||||
|
||||
const ConnectedTodoHeader = connect(
|
||||
state => {},
|
||||
state => ({}),
|
||||
(dispatch: any) => ({
|
||||
addTodo: label => dispatch(actionsWithService.addTodo(label)),
|
||||
setFilter: filter => dispatch(actions.setFilter(filter))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Stack, Checkbox, IconButton, TextField, DefaultButton } from 'office-ui-fabric-react';
|
||||
import { actions, actionsWithService } from '../actions';
|
||||
import { actionsWithService } from '../actions';
|
||||
import { Store } from '../store';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
@@ -24,9 +24,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const dispatch = this.context.dispatch;
|
||||
const { id, todos, complete, remove } = this.props;
|
||||
|
||||
const item = todos[id];
|
||||
|
||||
@@ -34,10 +32,10 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
<Stack horizontal verticalAlign="center" horizontalAlign="space-between">
|
||||
{!this.state.editing && (
|
||||
<>
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => dispatch(actions.complete(id))} />
|
||||
<Checkbox label={item.label} checked={item.completed} onChange={() => complete(id)} />
|
||||
<div>
|
||||
<IconButton iconProps={{ iconName: 'Edit' }} onClick={this.onEdit} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => dispatch(actions.remove(id))} />
|
||||
<IconButton iconProps={{ iconName: 'Cancel' }} onClick={() => remove(id)} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -57,8 +55,7 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
}
|
||||
|
||||
private onEdit = () => {
|
||||
const { id } = this.props;
|
||||
const { todos } = this.context.getState();
|
||||
const { id, todos } = this.props;
|
||||
const { label } = todos[id];
|
||||
|
||||
this.setState({
|
||||
@@ -83,9 +80,9 @@ class TodoListItem extends React.Component<TodoListItemProps, TodoListItemState>
|
||||
const ConnectedTodoListItem = connect(
|
||||
(state: Store) => ({ todos: state.todos }),
|
||||
(dispatch: any) => ({
|
||||
complete: label => dispatch(actionsWithService.addTodo(label)),
|
||||
remove: label => dispatch(actionsWithService.addTodo(label)),
|
||||
edit: filter => dispatch(actions.setFilter(filter))
|
||||
complete: id => dispatch(actionsWithService.complete(id)),
|
||||
remove: id => dispatch(actionsWithService.remove(id)),
|
||||
edit: (id, label) => dispatch(actionsWithService.edit(id, label))
|
||||
})
|
||||
)(TodoListItem);
|
||||
|
||||
|
||||
@@ -7,13 +7,12 @@ import { Provider } from 'react-redux';
|
||||
import { initializeIcons } from '@uifabric/icons';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import thunk from 'redux-thunk';
|
||||
import { FilterTypes } from './store';
|
||||
import * as service from './service';
|
||||
import { Store, FilterTypes } from './store';
|
||||
|
||||
(async () => {
|
||||
// TODO: to make the store pre-populate with data from the service,
|
||||
// replace the todos value below with a call to "await service.getAll()"
|
||||
const preloadStore = {
|
||||
todos: {},
|
||||
todos: (await service.getAll()) as Store['todos'],
|
||||
filter: 'all' as FilterTypes
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user