<FirstComponent meantForSomeChild='hi' />
<SecondComponent {...this.props} />
<ThirdComponent {...this.props} />
<FourthComponent {...this.props} />
<FifthComponent {...this.props} />
FinalComponent = (props) => <Button>{props.meantForSomeChild}</Button>
<ThirdComponent someSetting={props.justMe} />
React
React Native
import { AddPackingItem } from "packlist-components";
// VS
import { AddPackingItem } from 'packlist-components/native'
addItem, setNewItemText, value, clear
allItems
// Top level state
export default class App extends Component {
state = {
allItems: ["nachos", "burritos", "hot dog"],
newItemName: ""
};
// ...
}
export default class App extends Component {
// ...
// Top level functions
addItem = () => {
this.setState(state => ({
allItems: [...state.allItems, state.newItemName],
newItemName: ""
}));
};
// ...
}
//...
<AddItems
addItem={this.addItem}
setNewItemText={this.setNewItemName}
value={this.state.newItemName}
clear={this.clear}
/>
<ListItems allItems={this.state.allItems} />
//...
NO THRILLS! It's simple right?
It's a pain, too.
// Top level state
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
allItems: ["nachos", "burritos", "hot dog"],
newItemName: "",
addItem: this.addItem,
setNewItemName: this.setNewItemName,
clear: this.clear
};
}
// ...
}
export default class App extends Component {
// ...
// Top level functions
addItem = () => {
this.setState(state => ({
allItems: [...state.allItems, state.newItemName],
newItemName: ""
}));
};
// ...
}
//...
<PackingContext.Provider value={this.state}>
<AddItems />
<ListItems />
</PackingContext.Provider>
//...
<PackingContext.Consumer>
{({ newItemName, addItem, setNewItemName, clear }) => (
<AddPackingItem
addItem={addItem}
setNewItemText={setNewItemName}
value={newItemName}
clear={clear}
/>
)}
</PackingContext.Consumer>
import { PackingContext } from "Context/packingContext";
export const PackingDefaults = {
allItems: ["nacho", "burrito", "hotdog"],
newItemName: ""
};
export const PackingContext = React.createContext({
...PackingDefaults
});
export const PackingDefaults = {
allItems: ["nacho", "burrito", "hotdog"],
newItemName: ""
};
export const PackingContext = React.createContext({
...PackingDefaults
});
What will this look like if we try to consume context outside of a Provider?
export const PackingDefaults = {
allItems: ["nacho", "burrito", "hotdog"],
newItemName: ""
};
export const PackingContext = React.createContext({
...PackingDefaults
});
What will this look like if we try to consume context outside of a Provider?
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div style={styles}>
<h2>Welcome to Redux</h2>
<AddItems />
<ListItems />
</div>
</Provider>
);
}
}
SimpleList
import { SimpleList } from "packlist-components";
import { connect } from "react-redux";
// What props will this component get from Global state?
const mapStateToProps = state => ({ value: state.items.myItems });
// HOC lane
export default connect(mapStateToProps)(SimpleList);
The Higher Order Component trick
A higher order function is a function that takes a function as an argument or returns a function.
A higher order component is a function that takes a component as an argument AND returns a component.
SimpleList
import { SimpleList } from "packlist-components";
import { connect } from "react-redux";
// What props will this component get from Global state?
const mapStateToProps = state => ({ value: state.items.myItems });
// HOC lane
export default connect(mapStateToProps)(SimpleList);
We get a state-intelligent component wrapping our original component.
AddPackingItem
class AddItems extends Component {
render () {
const { dispatch, newItemName } = this.props
return (
<AddPackingItem
addItem={() => dispatch(ItemActionCreators.addItem())}
setNewItemText={(e) =>
dispatch(ItemActionCreators.setNewItemName(e.target.value))
}
value={newItemName}
clear={() => dispatch(ItemActionCreators.clear())}
/>
)
}
}
const mapStateToProps = state => ({newItemName: state.items.newItemName})
export default connect(mapStateToProps)(AddItems)
import { AddPackingItem } from "packlist-components";
import { ItemActionCreators } from "../Redux/Actions/items";
import { connect } from "react-redux";
const { setNewItemName, addItem, clear } = ItemActionCreators;
const mapDispatchToProps = {
setNewItemText: e => setNewItemName(e.target.value),
addItem,
clear
};
const mapStateToProps = state => ({ value: state.items.newItemName });
export default connect(mapStateToProps, mapDispatchToProps)(AddPackingItem);
export const ItemsActions = {
ADD_ITEM: 'ADD_ITEM',
CLEAR: 'CLEAR',
SET_NEW_ITEM_NAME: 'SET_NEW_ITEM_NAME'
}
export const ItemActionCreators = {
addItem: () => ({ type: ItemsActions.ADD_ITEM }),
clear: () => ({ type: ItemsActions.CLEAR}),
setNewItemName: (value) => ({
type: ItemsActions.SET_NEW_ITEM_NAME,
value
})
}
const INITIAL_STATE = { myItems: ['nacho', 'burrito', 'hotdog'],
newItemName: ''}
export function reducer (state = INITIAL_STATE, action) {
switch (action.type) {
case ItemsActions.ADD_ITEM:
return { ...state, myItems: [...state.myItems, state.newItemName],
newItemName: ''
}
case ItemsActions.CLEAR:
return { ...state, myItems: [] }
case ItemsActions.SET_NEW_ITEM_NAME:
return { ...state, newItemName: action.value }
default:
return state
}
}
import { createStore, applyMiddleware } from 'redux'
import RootReducer from '../Reducers'
let middleware = []
const baseStore = createStore(RootReducer,
applyMiddleware(...middleware))
export default initialState => {
return baseStore
}
export default class App extends Component {
render() {
return (
<div style={styles}>
<h2>Welcome to MobX</h2>
<AddItems />
<ListItems />
</div>
);
}
}
import React, { Component } from "react";
import { SimpleList } from "packlist-components";
import ListStore from "../Mobx/listStore";
import { observer } from "mobx-react";
@observer
export default class ListItems extends Component {
render() {
return <SimpleList value={[...ListStore.allItems]} />;
}
}
@observer
export default class AddItems extends Component {
render() {
return (
<AddPackingItem
addItem={ListStore.addItem}
setNewItemText={ListStore.setNewItemName}
value={ListStore.newItemName}
clear={ListStore.clear}
/>
);
}
}
class ObservableListStore {
@observable allItems = ["nacho", "burrito", "hotdog"];
@observable newItemName = "";
addItem = () => {
this.allItems.push(this.newItemName);
this.newItemName = "";
};
clear = () => {
this.allItems = [];
};
setNewItemName = e => {
this.newItemName = e.target.value;
};
}
The good suggestions and the bad suggestions!
import { Provider } from "unstated";
export default class App extends Component {
render() {
return (
<Provider>
<div style={styles}>
<h2>Welcome to unstated</h2>
<AddItems />
<ListItems />
</div>
</Provider>
);
}
}
import React, { Component } from "react";
import ListContainer from "../Unstated/listContainer";
import { Subscribe } from "unstated";
import { SimpleList } from "packlist-components";
export default class ListItems extends Component {
render() {
return (
<Subscribe to={[ListContainer]}>
{list => <SimpleList value={list.state.allItems} />}
</Subscribe>
);
}
}
<Subscribe to={[ListContainer]}>
{list => (
<AddPackingItem
addItem={list.addItem}
setNewItemText={list.setNewItemName}
value={list.state.newItemName}
clear={list.clear}
/>
)}
</Subscribe>
import { Container } from "unstated";
export default class ListContainer extends Container {
state = {
allItems: ["nachos", "burritos", "hot dog"],
newItemName: ""
};
addItem = () => {
this.setState(state => ({ allItems: [...state.allItems, state.newItemName],
newItemName: "" }));
};
setNewItemName = event => {
this.setState({ newItemName: event.target.value });
};
clear = () => {
this.setState({ allItems: [] });
};
}
import { Machine } from 'xstate';
const lightMachine = Machine({
key: 'light',
initial: 'green',
states: {
green: {
on: {
TIMER: 'yellow',
}
},
yellow: {
on: {
TIMER: 'red',
}
},
red: {
on: {
TIMER: 'green',
}
}
}
});
const currentState = 'green';
const nextState = lightMachine
.transition(currentState, 'TIMER')
.value;
export default {
initial: "idle",
states: {
idle: {
on: {
CLEAR: {
idle: {
actions: ["clear"],
cond: isTrue
}
},
SET_NEW_ITEM_NAME: {
loaded: {
actions: ["setNewItemName"]
}
}
}
},
loaded: {
on: {
ADD_ITEM: {
idle: {
actions: ["addItem"]
}
},
CLEAR: {
loaded: {
actions: ["clear"],
cond: isTrue
},
},
SET_NEW_ITEM_NAME: {
loaded: {
actions: ["setNewItemName"],
cond: isNotEmpty
},
idle: {
actions: ["setNewItemName"],
cond: isEmpty
}
}
}
}
}
};
<State is="idle">Current State 'idle'</State>
<State is="loaded">Current State 'loaded'</State>
import { testStateMachine } from 'react-automata'
import { App } from '../App'
test('all state snapshots', () => {
testStateMachine(App, { fixtures })
})
<Mutation
mutation={mutations.ADD_ITEMS}
variables={{ newItem: this.state.newItem }}
>
{addItem => (
<button style={styles.addButton} onClick={addItem}>
Add Item
</button>
)}
</Mutation>
<Mutation mutation={mutations.CLEAN_ITEMS}>
{cleanItems => (
<button style={styles.cleanButton} onClick={cleanItems}>
Clean Items
</button>
)}
</Mutation>
<ApolloProvider client={client}>
<div style={styles}>
<h2>Welcome to Apollo-Link-State</h2>
<AddItems />
<ListItems />
</div>
</ApolloProvider>
@graphql(addItem, { name: "addItem" })
@graphql(clearItems, { name: "clearItems" })
@graphql(updateNewItem, { name: "updateNewItem" })
@graphql(newItem)
export default class AddItems extends Component {
// ... just access props
// like `this.props.addItem`
}
const resolvers = {
Mutation: {
addItem: (_, _params, { cache }) => {
const { items, newItem } = cache.readQuery({
query: gql`
{
items @client
newItem @client
}
`
});
const data = { items: items.concat([newItem]), newItem: "" };
cache.writeData({ data });
return null;
},
clearItems: (_, _params, { cache }) => {
const data = { items: [] };
cache.writeData({ data });
return null;
},
updateNewItem: (_, { text }, { cache }) => {
const data = { newItem: text }
cache.writeData({ data });
return null;
},
}
};
import { gql } from "apollo-boost";
// Queries
export const items = gql`
{
items @client
}
`;
export const newItem = gql`
{
newItem @client
}
`;
Review them all!
~1hr to run
TO YOU!!!
https://slides.com/gantlaborde/react-state-museum