Local state is fine.
Redux [adds] indirection to decouple “what happened” from “how things change”
When we have multiple components that share common state
-- Vuex docs
- Component can get its own data, and manage its own state
- That state is changed in a single place
- Multiple components share the same data and state
- Both components may need to update this shared state
- The rest of the app doesn't know about the state, or how it's shared
- Components use some external method to trigger state changes
import React, { Component } from 'react';
class Counter extends Component {
state = { value: 0 };
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
decrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
import React, { Component } from 'react';
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
import React, { Component } from 'react';
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
const StatelessCounter = ({ value, increment, decrement }) => {
return (
<div>
{value}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
render() {
return (
<StatelessCounter value={this.state.value} increment={this.increment} decrement={decrement} />
);
}
}
- Describe application state as plain objects and arrays, single source of truth
- You can use an initial state object to describe the entire application
- Describe changes in the system as plain objects, state is read-only
- Actions are dispatched from component, or other actions
- Describe the logic for handling changes as pure functions
- State is updated using reducers, which accept an accumulation and a value and returns a new accumulation
{
app: {}, // application stuff...
transient: { // does not survive a refresh
selectedElement: 'element-d88c-4bbd-9453-db22e949b92e',
selectedPage: 'page-f3ce-4bb7-86c8-0417606d6592',
resolvedArgs: {
'element-d88c-4bbd-9453-db22e949b92e': {
expressionContexts: {
0: {
state: 'ready',
value: {
type: 'datatable',
columns: ['id', 'username', 'time', 'cost', 'price'],
},
error: null,
},
1: {
state: 'ready',
value: {
type: 'datatable',
columns: ['id', 'username', 'time', 'cost', 'price'],
},
error: null,
},
}
}
}
},
persistent: { // survives refresh, be serialized and be saved
workpad: {
name: 'Untitled Workpad',
id: 'workpad-2235-4af1-b781-24e42a453e5b',
pages: [{
id: 'page-f3ce-4bb7-86c8-0417606d6592',
elements: [{
id: 'element-d88c-4bbd-9453-db22e949b92e',
expression: 'demodata().sort("time").pointseries(x="time", y=.math("sum(price)")).line()',
ast: {
type: 'expression',
chain: ['...'],
}
}]
}]
}
}
}
- Redux treats async as an advanced topic
- You end up needing to dispatch other actions from your async actions
- There's a slew of middleware and even action creators to try to help
- Most rely on redux-thunk, which exposes
dispatch
andgetState
to actions- Then why use a helper at all?
- Most rely on redux-thunk, which exposes
- It seems like everyone writes their own helpers, just like I did
- Redux docs tell you to normalize your state
- Allowing reducers to modify the state tree without knowing the full shape
- You can use combineReducers to build the tree
- Mixes well with reduce-reducers to split reducer logic apart