Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions playground/src/connected/fc-counter-connected-extended.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { connect } from 'react-redux';

import { RootState } from '../store/root-reducer';
import { countersActions } from '../features/counters';
import { getCounter } from '../features/counters/selectors';
import FCCounter from '../components/fc-counter';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The component FCCounter is exported as a named export (export const FCCounter) in playground/src/components/fc-counter.tsx, not as a default export. You should use a named import here to avoid import errors.

Suggested change
import FCCounter from '../components/fc-counter';
import { FCCounter } from '../components/fc-counter';


// mapStateToProps using a selector to derive counter value from global state
// The selector handles the adaptation from RootState to the value the component needs
const mapStateToProps = (state: RootState) => ({
count: getCounter(state),
});

const mapDispatchToProps = {
onIncrement: countersActions.increment,
onDecrement: countersActions.decrement,
};
Comment on lines +15 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The FCCounter component's props do not include onDecrement. Passing onDecrement in mapDispatchToProps will cause a TypeScript compilation error when connecting the component.

const mapDispatchToProps = {
  onIncrement: countersActions.increment,
};


export default connect(
mapStateToProps,
mapDispatchToProps
)(FCCounter);
27 changes: 27 additions & 0 deletions playground/src/connected/todos-connected.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from 'react';
import { connect } from 'react-redux';

import { RootState } from '../store/root-reducer';
import { todosActions } from '../features/todos';
import {
getTodosFilteredList,
getTodosFilter,
} from '../features/todos/selectors';
import TodoList from '../components/todo-list';

// mapStateToProps using selectors to derive data from global state.
// Selectors encapsulate the knowledge of the state shape and
// any derived computations (e.g., filtering).
const mapStateToProps = (state: RootState) => ({
todos: getTodosFilteredList(state),
filter: getTodosFilter(state),
});

const mapDispatchToProps = {
toggle: todosActions.toggle,
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
30 changes: 28 additions & 2 deletions playground/src/features/counters/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
import { CountersState } from './reducer';
import { createSelector } from 'reselect';
import { RootState } from '../../store/root-reducer';

export const getReduxCounter = (state: CountersState) => state.reduxCounter;
/**
* Selectors are used to derive data from the Redux store state.
* They are composed using `createSelector` from the `reselect` library.
*
* To adapt global state (RootState) to feature-specific state, we use
* a "input selector" that picks the feature slice from the global state.
*/

// Input selector: picks the counters feature slice from global state
const getCounters = (state: RootState) => state.counters;

// Derived selectors
export const getCountersState = createSelector(
getCounters,
counters => counters
);

export const getCounter = createSelector(
getCounters,
counters => counters.counter
);
Comment on lines +21 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The state shape defined in CountersState has the property reduxCounter, not counter. Accessing counters.counter will result in a compilation error or undefined at runtime.

Suggested change
export const getCounter = createSelector(
getCounters,
counters => counters.counter
);
export const getCounter = createSelector(
getCounters,
counters => counters.reduxCounter
);


export const getCounterHasValue = createSelector(
getCounter,
counter => counter > 0
);
51 changes: 38 additions & 13 deletions playground/src/features/todos/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
import { createSelector } from 'reselect';
import { RootState } from '../../store/root-reducer';

import { TodosState } from './reducer';
/**
* Selectors are used to derive data from the Redux store state.
* They are composed using `createSelector` from the `reselect` library.
*
* To adapt global state (RootState) to feature-specific state, we use
* a "input selector" that picks the feature slice from the global state.
*/

export const getTodos = (state: TodosState) => state.todos;
// Input selector: picks the todos feature slice from global state
const getTodos = (state: RootState) => state.todos;

export const getTodosFilter = (state: TodosState) => state.todosFilter;
// Derived selectors
export const getTodosState = createSelector(
getTodos,
todos => todos
);

export const getFilteredTodos = createSelector(getTodos, getTodosFilter, (todos, todosFilter) => {
switch (todosFilter) {
case 'completed':
return todos.filter(t => t.completed);
case 'active':
return todos.filter(t => !t.completed);
export const getTodosList = createSelector(
getTodos,
todos => todos.todos
);

default:
return todos;
}
});
export const getTodosFilter = createSelector(
getTodos,
todos => todos.filter
);
Comment on lines +26 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The state shape defined in TodosState has the property todosFilter, not filter. Accessing todos.filter will return undefined instead of the filter value.

Suggested change
export const getTodosFilter = createSelector(
getTodos,
todos => todos.filter
);
export const getTodosFilter = createSelector(
getTodos,
todos => todos.todosFilter
);


export const getTodosFilteredList = createSelector(
getTodosList,
getTodosFilter,
(todos, filter) => todos.filter(todo => {
switch (filter) {
case 'active':
return !todo.done;
case 'completed':
return todo.done;
default:
return true;
}
})
Comment on lines +34 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Todo items in the state use the property completed to track completion status, not done. Accessing todo.done will result in incorrect filtering behavior because it will be undefined.

Suggested change
(todos, filter) => todos.filter(todo => {
switch (filter) {
case 'active':
return !todo.done;
case 'completed':
return todo.done;
default:
return true;
}
})
(todos, filter) => todos.filter(todo => {
switch (filter) {
case 'active':
return !todo.completed;
case 'completed':
return todo.completed;
default:
return true;
}
})

);