mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-25 19:13:56 +03:00
feat(redux-devtools): convert todomvc example to TypeScript (#618)
This commit is contained in:
parent
f1e3f4f834
commit
37191e46e6
|
@ -13,6 +13,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^3.9.0",
|
||||
"@typescript-eslint/parser": "^3.9.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-babel": "^5.3.1",
|
||||
|
@ -22,7 +23,9 @@
|
|||
"jest": "^26.2.2",
|
||||
"lerna": "^3.22.1",
|
||||
"prettier": "^2.0.5",
|
||||
"raw-loader": "^4.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-jest": "^26.2.0",
|
||||
"ts-node": "^9.0.0",
|
||||
"typescript": "^3.9.7",
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.react.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.react.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-function-bind",
|
||||
"react-hot-loader/babel"
|
||||
]
|
||||
}
|
||||
|
|
21
packages/redux-devtools/examples/todomvc/.eslintrc.js
Normal file
21
packages/redux-devtools/examples/todomvc/.eslintrc.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
extends: '../../../../.eslintrc',
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: '../../../../eslintrc.ts.react.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['webpack.config.ts'],
|
||||
extends: '../../../../eslintrc.ts.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.webpack.json'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
import * as types from '../constants/ActionTypes';
|
||||
|
||||
export function addTodo(text) {
|
||||
return {
|
||||
type: types.ADD_TODO,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteTodo(id) {
|
||||
return {
|
||||
type: types.DELETE_TODO,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
export function editTodo(id, text) {
|
||||
return {
|
||||
type: types.EDIT_TODO,
|
||||
id,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
export function markTodo(id) {
|
||||
return {
|
||||
type: types.MARK_TODO,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
export function markAll() {
|
||||
return {
|
||||
type: types.MARK_ALL,
|
||||
};
|
||||
}
|
||||
|
||||
export function clearMarked() {
|
||||
return {
|
||||
type: types.CLEAR_MARKED,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import * as types from '../constants/ActionTypes';
|
||||
|
||||
interface AddTodoAction {
|
||||
type: typeof types.ADD_TODO;
|
||||
text: string;
|
||||
}
|
||||
export function addTodo(text: string): AddTodoAction {
|
||||
return {
|
||||
type: types.ADD_TODO,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
interface DeleteTodoAction {
|
||||
type: typeof types.DELETE_TODO;
|
||||
id: number;
|
||||
}
|
||||
export function deleteTodo(id: number): DeleteTodoAction {
|
||||
return {
|
||||
type: types.DELETE_TODO,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
interface EditTodoAction {
|
||||
type: typeof types.EDIT_TODO;
|
||||
id: number;
|
||||
text: string;
|
||||
}
|
||||
export function editTodo(id: number, text: string): EditTodoAction {
|
||||
return {
|
||||
type: types.EDIT_TODO,
|
||||
id,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
interface MarkTodoAction {
|
||||
type: typeof types.MARK_TODO;
|
||||
id: number;
|
||||
}
|
||||
export function markTodo(id: number): MarkTodoAction {
|
||||
return {
|
||||
type: types.MARK_TODO,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
interface MarkAllAction {
|
||||
type: typeof types.MARK_ALL;
|
||||
}
|
||||
export function markAll(): MarkAllAction {
|
||||
return {
|
||||
type: types.MARK_ALL,
|
||||
};
|
||||
}
|
||||
|
||||
interface ClearMarkedAction {
|
||||
type: typeof types.CLEAR_MARKED;
|
||||
}
|
||||
export function clearMarked(): ClearMarkedAction {
|
||||
return {
|
||||
type: types.CLEAR_MARKED,
|
||||
};
|
||||
}
|
||||
|
||||
export type TodoAction =
|
||||
| AddTodoAction
|
||||
| DeleteTodoAction
|
||||
| EditTodoAction
|
||||
| MarkTodoAction
|
||||
| MarkAllAction
|
||||
| ClearMarkedAction;
|
||||
|
||||
export interface TodoActions {
|
||||
addTodo(text: string): AddTodoAction;
|
||||
deleteTodo(id: number): DeleteTodoAction;
|
||||
editTodo(id: number, text: string): EditTodoAction;
|
||||
markTodo(id: number): MarkTodoAction;
|
||||
markAll(): MarkAllAction;
|
||||
clearMarked(): ClearMarkedAction;
|
||||
}
|
|
@ -1,7 +1,12 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component, MouseEventHandler } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
|
||||
import {
|
||||
SHOW_ALL,
|
||||
SHOW_MARKED,
|
||||
SHOW_UNMARKED,
|
||||
TodoFilter,
|
||||
} from '../constants/TodoFilters';
|
||||
|
||||
const FILTER_TITLES = {
|
||||
[SHOW_ALL]: 'All',
|
||||
|
@ -9,7 +14,15 @@ const FILTER_TITLES = {
|
|||
[SHOW_MARKED]: 'Completed',
|
||||
};
|
||||
|
||||
export default class Footer extends Component {
|
||||
interface Props {
|
||||
markedCount: number;
|
||||
unmarkedCount: number;
|
||||
filter: TodoFilter;
|
||||
onClearMarked: MouseEventHandler<HTMLButtonElement>;
|
||||
onShow: (filter: TodoFilter) => void;
|
||||
}
|
||||
|
||||
export default class Footer extends Component<Props> {
|
||||
static propTypes = {
|
||||
markedCount: PropTypes.number.isRequired,
|
||||
unmarkedCount: PropTypes.number.isRequired,
|
||||
|
@ -23,7 +36,7 @@ export default class Footer extends Component {
|
|||
<footer className="footer">
|
||||
{this.renderTodoCount()}
|
||||
<ul className="filters">
|
||||
{[SHOW_ALL, SHOW_UNMARKED, SHOW_MARKED].map((filter) => (
|
||||
{([SHOW_ALL, SHOW_UNMARKED, SHOW_MARKED] as const).map((filter) => (
|
||||
<li key={filter}>{this.renderFilterLink(filter)}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -43,7 +56,7 @@ export default class Footer extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderFilterLink(filter) {
|
||||
renderFilterLink(filter: TodoFilter) {
|
||||
const title = FILTER_TITLES[filter];
|
||||
const { filter: selectedFilter, onShow } = this.props;
|
||||
|
|
@ -2,16 +2,20 @@ import React, { Component } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import TodoTextInput from './TodoTextInput';
|
||||
|
||||
export default class Header extends Component {
|
||||
interface Props {
|
||||
addTodo: (text: string) => void;
|
||||
}
|
||||
|
||||
export default class Header extends Component<Props> {
|
||||
static propTypes = {
|
||||
addTodo: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
handleSave(text) {
|
||||
handleSave = (text: string) => {
|
||||
if (text.length !== 0) {
|
||||
this.props.addTodo(text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -19,7 +23,7 @@ export default class Header extends Component {
|
|||
<h1>todos</h1>
|
||||
<TodoTextInput
|
||||
newTodo={true}
|
||||
onSave={::this.handleSave}
|
||||
onSave={this.handleSave}
|
||||
placeholder="What needs to be done?"
|
||||
/>
|
||||
</header>
|
|
@ -1,16 +1,32 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component, MouseEventHandler } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TodoItem from './TodoItem';
|
||||
import Footer from './Footer';
|
||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
|
||||
import {
|
||||
SHOW_ALL,
|
||||
SHOW_MARKED,
|
||||
SHOW_UNMARKED,
|
||||
TodoFilter,
|
||||
} from '../constants/TodoFilters';
|
||||
import { Todo } from '../reducers/todos';
|
||||
import { TodoActions } from '../actions/TodoActions';
|
||||
|
||||
const TODO_FILTERS = {
|
||||
[SHOW_ALL]: () => true,
|
||||
[SHOW_UNMARKED]: (todo) => !todo.marked,
|
||||
[SHOW_MARKED]: (todo) => todo.marked,
|
||||
[SHOW_UNMARKED]: (todo: Todo) => !todo.marked,
|
||||
[SHOW_MARKED]: (todo: Todo) => todo.marked,
|
||||
};
|
||||
|
||||
export default class MainSection extends Component {
|
||||
interface State {
|
||||
filter: TodoFilter;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
todos: Todo[];
|
||||
actions: TodoActions;
|
||||
}
|
||||
|
||||
export default class MainSection extends Component<Props, State> {
|
||||
static propTypes = {
|
||||
todos: PropTypes.array.isRequired,
|
||||
actions: PropTypes.object.isRequired,
|
||||
|
@ -18,22 +34,19 @@ export default class MainSection extends Component {
|
|||
// Keep a counter that can be used to create an html `id` attribute.
|
||||
static mountCount = 0;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = { filter: SHOW_ALL };
|
||||
this.htmlFormInputId = `toggle-all-${MainSection.mountCount++}`;
|
||||
}
|
||||
state: State = { filter: SHOW_ALL };
|
||||
htmlFormInputId = `toggle-all-${MainSection.mountCount++}`;
|
||||
|
||||
handleClearMarked() {
|
||||
handleClearMarked: MouseEventHandler<HTMLButtonElement> = () => {
|
||||
const atLeastOneMarked = this.props.todos.some((todo) => todo.marked);
|
||||
if (atLeastOneMarked) {
|
||||
this.props.actions.clearMarked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShow(filter) {
|
||||
handleShow = (filter: TodoFilter) => {
|
||||
this.setState({ filter });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { todos, actions } = this.props;
|
||||
|
@ -58,7 +71,7 @@ export default class MainSection extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderToggleAll(markedCount) {
|
||||
renderToggleAll(markedCount: number) {
|
||||
const { todos, actions } = this.props;
|
||||
|
||||
if (todos.length > 0) {
|
||||
|
@ -69,6 +82,7 @@ export default class MainSection extends Component {
|
|||
className="toggle-all"
|
||||
type="checkbox"
|
||||
checked={markedCount === todos.length}
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
onChange={actions.markAll}
|
||||
/>
|
||||
<label htmlFor={this.htmlFormInputId}>Mark all as complete</label>
|
||||
|
@ -77,7 +91,7 @@ export default class MainSection extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
renderFooter(markedCount) {
|
||||
renderFooter(markedCount: number) {
|
||||
const { todos } = this.props;
|
||||
const { filter } = this.state;
|
||||
const unmarkedCount = todos.length - markedCount;
|
||||
|
@ -88,8 +102,8 @@ export default class MainSection extends Component {
|
|||
markedCount={markedCount}
|
||||
unmarkedCount={unmarkedCount}
|
||||
filter={filter}
|
||||
onClearMarked={::this.handleClearMarked}
|
||||
onShow={::this.handleShow}
|
||||
onClearMarked={this.handleClearMarked}
|
||||
onShow={this.handleShow}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -2,8 +2,23 @@ import React, { Component } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import TodoTextInput from './TodoTextInput';
|
||||
import { Todo } from '../reducers/todos';
|
||||
|
||||
export default class TodoItem extends Component {
|
||||
interface State {
|
||||
editing: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
todo: Todo;
|
||||
addTodo: (text: string) => void;
|
||||
deleteTodo: (id: number) => void;
|
||||
editTodo: (id: number, text: string) => void;
|
||||
markTodo: (id: number) => void;
|
||||
markAll: () => void;
|
||||
clearMarked: () => void;
|
||||
}
|
||||
|
||||
export default class TodoItem extends Component<Props, State> {
|
||||
static propTypes = {
|
||||
todo: PropTypes.object.isRequired,
|
||||
editTodo: PropTypes.func.isRequired,
|
||||
|
@ -11,18 +26,15 @@ export default class TodoItem extends Component {
|
|||
markTodo: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
editing: false,
|
||||
};
|
||||
}
|
||||
state: State = {
|
||||
editing: false,
|
||||
};
|
||||
|
||||
handleDoubleClick() {
|
||||
handleDoubleClick = () => {
|
||||
this.setState({ editing: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleSave(id, text) {
|
||||
handleSave(id: number, text: string) {
|
||||
if (text.length === 0) {
|
||||
this.props.deleteTodo(id);
|
||||
} else {
|
||||
|
@ -52,7 +64,7 @@ export default class TodoItem extends Component {
|
|||
checked={todo.marked}
|
||||
onChange={() => markTodo(todo.id)}
|
||||
/>
|
||||
<label onDoubleClick={::this.handleDoubleClick}>{todo.text}</label>
|
||||
<label onDoubleClick={this.handleDoubleClick}>{todo.text}</label>
|
||||
<button className="destroy" onClick={() => deleteTodo(todo.id)} />
|
||||
</div>
|
||||
);
|
|
@ -1,8 +1,25 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, {
|
||||
ChangeEventHandler,
|
||||
Component,
|
||||
FocusEventHandler,
|
||||
KeyboardEventHandler,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
export default class TodoTextInput extends Component {
|
||||
interface State {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onSave: (text: string) => void;
|
||||
text?: string;
|
||||
placeholder?: string;
|
||||
editing?: boolean;
|
||||
newTodo?: boolean;
|
||||
}
|
||||
|
||||
export default class TodoTextInput extends Component<Props, State> {
|
||||
static propTypes = {
|
||||
onSave: PropTypes.func.isRequired,
|
||||
text: PropTypes.string,
|
||||
|
@ -11,32 +28,29 @@ export default class TodoTextInput extends Component {
|
|||
newTodo: PropTypes.bool,
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
text: this.props.text || '',
|
||||
};
|
||||
}
|
||||
state = {
|
||||
text: this.props.text || '',
|
||||
};
|
||||
|
||||
handleSubmit(e) {
|
||||
const text = e.target.value.trim();
|
||||
handleSubmit: KeyboardEventHandler<HTMLInputElement> = (e) => {
|
||||
const text = e.currentTarget.value.trim();
|
||||
if (e.which === 13) {
|
||||
this.props.onSave(text);
|
||||
if (this.props.newTodo) {
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleChange(e) {
|
||||
handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
this.setState({ text: e.target.value });
|
||||
}
|
||||
};
|
||||
|
||||
handleBlur(e) {
|
||||
handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
if (!this.props.newTodo) {
|
||||
this.props.onSave(e.target.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -49,9 +63,9 @@ export default class TodoTextInput extends Component {
|
|||
placeholder={this.props.placeholder}
|
||||
autoFocus={true}
|
||||
value={this.state.text}
|
||||
onBlur={::this.handleBlur}
|
||||
onChange={::this.handleChange}
|
||||
onKeyDown={::this.handleSubmit}
|
||||
onBlur={this.handleBlur}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleSubmit}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,3 +1,8 @@
|
|||
export const SHOW_ALL = 'show_all';
|
||||
export const SHOW_MARKED = 'show_marked';
|
||||
export const SHOW_UNMARKED = 'show_unmarked';
|
||||
|
||||
export type TodoFilter =
|
||||
| typeof SHOW_ALL
|
||||
| typeof SHOW_MARKED
|
||||
| typeof SHOW_UNMARKED;
|
|
@ -3,8 +3,15 @@ import React, { Component } from 'react';
|
|||
import { Provider } from 'react-redux';
|
||||
import TodoApp from './TodoApp';
|
||||
import DevTools from './DevTools';
|
||||
import { Store } from 'redux';
|
||||
import { TodoState } from '../reducers';
|
||||
import { TodoAction } from '../actions/TodoActions';
|
||||
|
||||
class Root extends Component {
|
||||
interface Props {
|
||||
store: Store<TodoState, TodoAction>;
|
||||
}
|
||||
|
||||
class Root extends Component<Props> {
|
||||
render() {
|
||||
const { store } = this.props;
|
||||
return (
|
|
@ -1,5 +0,0 @@
|
|||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./Root.prod');
|
||||
} else {
|
||||
module.exports = require('./Root.dev');
|
||||
}
|
|
@ -2,8 +2,15 @@ import { hot } from 'react-hot-loader/root';
|
|||
import React, { Component } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import TodoApp from './TodoApp';
|
||||
import { Store } from 'redux';
|
||||
import { TodoState } from '../reducers';
|
||||
import { TodoAction } from '../actions/TodoActions';
|
||||
|
||||
class Root extends Component {
|
||||
interface Props {
|
||||
store: Store<TodoState, TodoAction>;
|
||||
}
|
||||
|
||||
class Root extends Component<Props> {
|
||||
render() {
|
||||
const { store } = this.props;
|
||||
return (
|
15
packages/redux-devtools/examples/todomvc/containers/Root.ts
Normal file
15
packages/redux-devtools/examples/todomvc/containers/Root.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Store } from 'redux';
|
||||
import { TodoState } from '../reducers';
|
||||
import { TodoAction } from '../actions/TodoActions';
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
interface Props {
|
||||
store: Store<TodoState, TodoAction>;
|
||||
}
|
||||
const Root: ComponentType<Props> =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('./Root.prod').default
|
||||
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('./Root.dev').default;
|
||||
export default Root;
|
|
@ -1,16 +1,28 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import Header from '../components/Header';
|
||||
import MainSection from '../components/MainSection';
|
||||
import * as TodoActions from '../actions/TodoActions';
|
||||
import {
|
||||
TodoAction,
|
||||
TodoActions as TodoActionsType,
|
||||
} from '../actions/TodoActions';
|
||||
import { TodoState } from '../reducers';
|
||||
import { Todo } from '../reducers/todos';
|
||||
|
||||
class TodoApp extends Component {
|
||||
interface Props {
|
||||
todos: Todo[];
|
||||
actions: TodoActionsType;
|
||||
}
|
||||
|
||||
class TodoApp extends Component<Props> {
|
||||
render() {
|
||||
const { todos, actions } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* eslint-disable-next-line @typescript-eslint/unbound-method */}
|
||||
<Header addTodo={actions.addTodo} />
|
||||
<MainSection todos={todos} actions={actions} />
|
||||
</div>
|
||||
|
@ -18,13 +30,13 @@ class TodoApp extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function mapState(state) {
|
||||
function mapState(state: TodoState) {
|
||||
return {
|
||||
todos: state.todos,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatch(dispatch) {
|
||||
function mapDispatch(dispatch: Dispatch<TodoAction>) {
|
||||
return {
|
||||
actions: bindActionCreators(TodoActions, dispatch),
|
||||
};
|
|
@ -16,6 +16,7 @@ render(
|
|||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./containers/Root', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const RootContainer = require('./containers/Root').default;
|
||||
render(
|
||||
<AppContainer>
|
|
@ -2,15 +2,6 @@
|
|||
"name": "todomvc",
|
||||
"version": "0.0.1",
|
||||
"description": "TodoMVC example for redux",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --open"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"reactjs",
|
||||
|
@ -23,11 +14,22 @@
|
|||
"flux",
|
||||
"todomvc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools/examples/todomvc",
|
||||
"bugs": {
|
||||
"url": "https://github.com/reduxjs/redux-devtools/issues"
|
||||
},
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools#readme",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --open",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"type-check": "tsc --noEmit",
|
||||
"type-check:watch": "npm run type-check -- --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"prop-types": "^15.7.2",
|
||||
|
@ -36,21 +38,18 @@
|
|||
"react-hot-loader": "^4.12.21",
|
||||
"react-redux": "^7.2.1",
|
||||
"redux": "^4.0.5",
|
||||
"todomvc-app-css": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/plugin-proposal-function-bind": "^7.10.5",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/preset-react": "^7.10.4",
|
||||
"babel-loader": "^8.1.0",
|
||||
"raw-loader": "^4.0.1",
|
||||
"redux-devtools": "^3.6.1",
|
||||
"redux-devtools-dock-monitor": "^1.1.4",
|
||||
"redux-devtools-log-monitor": "^2.0.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
}
|
||||
"todomvc-app-css": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"@types/react": "^16.9.46",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/webpack-env": "^1.15.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import todos from './todos';
|
||||
import todos, { Todo } from './todos';
|
||||
|
||||
export interface TodoState {
|
||||
todos: Todo[];
|
||||
}
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
todos,
|
|
@ -6,8 +6,15 @@ import {
|
|||
MARK_ALL,
|
||||
CLEAR_MARKED,
|
||||
} from '../constants/ActionTypes';
|
||||
import { TodoAction } from '../actions/TodoActions';
|
||||
|
||||
const initialState = [
|
||||
export interface Todo {
|
||||
text: string;
|
||||
marked: boolean;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const initialState: Todo[] = [
|
||||
{
|
||||
text: 'Use Redux',
|
||||
marked: false,
|
||||
|
@ -15,7 +22,7 @@ const initialState = [
|
|||
},
|
||||
];
|
||||
|
||||
export default function todos(state = initialState, action) {
|
||||
export default function todos(state = initialState, action: TodoAction) {
|
||||
switch (action.type) {
|
||||
case ADD_TODO:
|
||||
return [
|
||||
|
@ -49,7 +56,7 @@ export default function todos(state = initialState, action) {
|
|||
}
|
||||
|
||||
case CLEAR_MARKED:
|
||||
return state.filter((todo) => todo.marked === false);
|
||||
return state.filter((todo) => !todo.marked);
|
||||
|
||||
default:
|
||||
return state;
|
|
@ -1,21 +0,0 @@
|
|||
import { createStore, compose } from 'redux';
|
||||
import { persistState } from 'redux-devtools';
|
||||
import rootReducer from '../reducers';
|
||||
import DevTools from '../containers/DevTools';
|
||||
|
||||
const enhancer = compose(
|
||||
DevTools.instrument(),
|
||||
persistState(window.location.href.match(/[?&]debug_session=([^&#]+)\b/))
|
||||
);
|
||||
|
||||
export default function configureStore(initialState) {
|
||||
const store = createStore(rootReducer, initialState, enhancer);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
store.replaceReducer(require('../reducers').default)
|
||||
);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { createStore, compose, PreloadedState } from 'redux';
|
||||
import { persistState } from 'redux-devtools';
|
||||
import rootReducer, { TodoState } from '../reducers';
|
||||
import DevTools from '../containers/DevTools';
|
||||
|
||||
function getDebugSessionKey() {
|
||||
const matches = /[?&]debug_session=([^&#]+)\b/.exec(window.location.href);
|
||||
return matches && matches.length > 0 ? matches[1] : null;
|
||||
}
|
||||
|
||||
const enhancer = compose(
|
||||
DevTools.instrument(),
|
||||
persistState(getDebugSessionKey())
|
||||
);
|
||||
|
||||
export default function configureStore(
|
||||
initialState?: PreloadedState<TodoState>
|
||||
) {
|
||||
const store = createStore(rootReducer, initialState, enhancer);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
store.replaceReducer(require('../reducers').default)
|
||||
);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./configureStore.prod');
|
||||
} else {
|
||||
module.exports = require('./configureStore.dev');
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import { createStore } from 'redux';
|
||||
import rootReducer from '../reducers';
|
||||
|
||||
export default function configureStore(initialState) {
|
||||
return createStore(rootReducer, initialState);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { createStore, PreloadedState } from 'redux';
|
||||
import rootReducer, { TodoState } from '../reducers';
|
||||
|
||||
export default function configureStore(
|
||||
initialState?: PreloadedState<TodoState>
|
||||
) {
|
||||
return createStore(rootReducer, initialState);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { PreloadedState, Store } from 'redux';
|
||||
import { TodoState } from '../reducers';
|
||||
import { TodoAction } from '../actions/TodoActions';
|
||||
|
||||
const configureStore: (
|
||||
initialState?: PreloadedState<TodoState>
|
||||
) => Store<TodoState, TodoAction> =
|
||||
process.env.NODE_ENV === 'production'
|
||||
? // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('./configureStore.prod').default
|
||||
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('./configureStore.dev').default;
|
||||
export default configureStore;
|
4
packages/redux-devtools/examples/todomvc/tsconfig.json
Normal file
4
packages/redux-devtools/examples/todomvc/tsconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.react.base.json",
|
||||
"include": ["."]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"include": ["webpack.config.ts"]
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
import * as path from 'path';
|
||||
import * as webpack from 'webpack';
|
||||
|
||||
module.exports = {
|
||||
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
|
||||
devtool: 'eval-source-map',
|
||||
entry: [
|
||||
'webpack-dev-server/client?http://localhost:3000',
|
||||
'webpack/hot/only-dev-server',
|
||||
|
@ -14,11 +13,10 @@ module.exports = {
|
|||
filename: 'bundle.js',
|
||||
publicPath: '/static/',
|
||||
},
|
||||
plugins: [new webpack.HotModuleReplacementPlugin()],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
test: /\.(js|ts)x?$/,
|
||||
loaders: ['babel-loader'],
|
||||
exclude: /node_modules/,
|
||||
include: __dirname,
|
||||
|
@ -37,9 +35,14 @@ module.exports = {
|
|||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
plugins: [new webpack.HotModuleReplacementPlugin()],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
port: 3000,
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
};
|
15
yarn.lock
15
yarn.lock
|
@ -403,14 +403,6 @@
|
|||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
|
||||
|
||||
"@babel/plugin-proposal-function-bind@^7.10.5":
|
||||
version "7.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.10.5.tgz#62acbdde1c43e7dfae6efc9ddd5bc60920cee719"
|
||||
integrity sha512-1lYbE2ynV9yN0LCEYCdEBD5pR6GaNkRfjn1z1tWDdWMJgunTFcJBZDJUgiMPcTMqAc3D6Vrm8v2khxjjx6FrCg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
"@babel/plugin-syntax-function-bind" "^7.10.4"
|
||||
|
||||
"@babel/plugin-proposal-json-strings@^7.0.0", "@babel/plugin-proposal-json-strings@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db"
|
||||
|
@ -563,13 +555,6 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
|
||||
"@babel/plugin-syntax-function-bind@^7.10.4":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.10.4.tgz#8378d94f3185ddd3008310c15fe0991cb0c85151"
|
||||
integrity sha512-vF/K9yS0dpPNlT7mXSGhbdpb2f4DaLa/AYYbUqlxOggAug/oseIR1+LgAzwci4iJNlqWNmJ7aQ+llUMYjn9uhw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
|
||||
"@babel/plugin-syntax-import-meta@^7.8.3":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
|
||||
|
|
Loading…
Reference in New Issue
Block a user