Commit 58060562 authored by Kristian Freeman's avatar Kristian Freeman

Answer key - redux

parent d63e0a71
const assert = require('assert');
const assert = require("assert");
const initialState = {
items: [],
items: []
};
// No changes are needed above this line.
const MY_CONSTANT = 'MY_CONSTANT';
const MY_CONSTANT = "MY_CONSTANT";
const ADD_ITEM = "ADD_ITEM";
const UPDATE_ITEM = "UPDATE_ITEM";
// Define two action constants: ADD_ITEM
// and UPDATE_ITEM.
// Replace the string 'replace me' with your
// new action constant for this method.
const addItem = item => {
return {type: 'replace me', item};
return { type: ADD_ITEM, item };
};
// Replace the string 'replace me' with your
// new action constant for this method.
const updateItem = (item, index) => {
return {type: 'replace me', item, index};
return { type: UPDATE_ITEM, item, index };
};
function itemsReducer(state = [], action) {
switch (action.type) {
// Replace the 'replace me' text with
// the correct action constant for
// this action.
case 'replace me':
// Adding an item
case ADD_ITEM:
return [].concat(state, action.item);
// Replace the 'replace me' text with
// the correct action constant for
// this action.
case 'replace me':
// Updating an item
case UPDATE_ITEM:
const newItems = [].concat(state);
newItems[action.index] = action.item;
return newItems;
......@@ -46,16 +33,10 @@ function itemsReducer(state = [], action) {
function appReducer(state = initialState, action) {
switch (action.type) {
// Replace both 'replace me' texts with
// the two action constants you've defined
// in this exercise. Note that putting
// two case statements in a row means
// that the first statement will "pass through"
// and use the same functionality as the second.
case 'replace me':
case 'replace me':
case ADD_ITEM:
case UPDATE_ITEM:
return Object.assign({}, state, {
items: itemsReducer(state.items, action),
items: itemsReducer(state.items, action)
});
default:
return state;
......@@ -65,43 +46,43 @@ function appReducer(state = initialState, action) {
// No changes are needed below this line.
let itemsState = itemsReducer([], {});
let newItemState = itemsReducer(itemsState, {action: 'UNKNOWN'});
let newItemState = itemsReducer(itemsState, { action: "UNKNOWN" });
assert.notEqual(
itemsState,
undefined,
"The itemsReducer isn't properly returning state - the new state is undefined",
"The itemsReducer isn't properly returning state - the new state is undefined"
);
itemsState = itemsReducer([], {});
itemsState = itemsReducer(itemsState, addItem('New item'));
itemsState = itemsReducer(itemsState, addItem("New item"));
assert.deepEqual(
itemsState,
['New item'],
"The itemsReducer didn't create a new item, given the ADD_ITEM action",
["New item"],
"The itemsReducer didn't create a new item, given the ADD_ITEM action"
);
const updateAction = updateItem('Newer item', 0);
const updateAction = updateItem("Newer item", 0);
itemsState = itemsReducer(itemsState, updateAction);
assert.deepEqual(
itemsState,
['Newer item'],
["Newer item"],
`The itemsReducer didn't update an item, given the UPDATE_ITEM action. Got ${
updateAction.type
} instead`,
} instead`
);
let state = appReducer(initialState, {});
state = appReducer(state, addItem('New item'));
state = appReducer(state, updateItem('Newer item', 0));
state = appReducer(state, addItem("New item"));
state = appReducer(state, updateItem("Newer item", 0));
assert.deepEqual(
state.items,
['Newer item'],
"The appReducer didn't update an item, given the UPDATE_ITEM action",
["Newer item"],
"The appReducer didn't update an item, given the UPDATE_ITEM action"
);
assert.notEqual(
state,
undefined,
"The appReducer isn't properly returning state - the new state is undefined",
"The appReducer isn't properly returning state - the new state is undefined"
);
console.log('Passed! :)');
console.log("Passed! :)");
import React, {Component} from 'react';
import React, { Component } from "react";
import { connect } from "react-redux";
import { decrement, increment } from "./actions";
// Import the named function connect from
// 'react-redux'.
class App extends Component {
render() {
const { counter, decrement, increment } = this.props;
// Import the decrement and increment functions
// from ./actions.
return (
<div>
<p>{counter}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
}
// Define an App component, that extends Component.
// The render function needs to have:
// - a p tag that displays the counter prop
// - a button with the text "Increment" that calls
// this.props.increment on click
// - a button with the text "Decrement" that calls
// this.props.decrement on click
const mapStateToProps = state => ({
counter: state.counter
});
// Define mapStateToProps, a function that takes the
// single argument state.
// This function should return a JS object with counter
// set to state.counter.
const mapDispatchToProps = { decrement, increment };
// Define mapDispatchToProps, which returns a JS object
// with decrement and increment.
// You can use the ES6 shorthand, where the key and value
// are equivalent (for instance, { key } makes { key: key }).
// Use the connect function, which is in the following format:
// connect(mapStateToProps, mapDispatchToProps)(ComponentName).
// Export this at the default export for this file.
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
// Import the named class Provider from 'react-redux'
import { Provider } from "react-redux";
import {createStore} from 'redux';
import reducer from './reducer';
import { createStore } from "redux";
import reducer from "./reducer";
// Define a store variable that is the result of calling the createStore
// function with a single argument, reducer.
const store = createStore(reducer);
// We're currently rendering <App />. With 'react-redux',
// we want to "wrap" that component inside the Provider component,
// which takes a single prop, store.
//
// Instead of directly rendering <App />, make <App /> the child element
// of a <Provider /> component, and render that using ReactDOM.render.
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
registerServiceWorker();
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
import reducers, {addItem} from './reducers';
import {createStore} from 'redux';
import reducers, { addItem } from "./reducers";
import { createStore } from "redux";
export const store = createStore(reducers);
class ConnectedComponent extends React.Component {
constructor() {
super();
// Create a state object with an empty items array.
// Bind the function this.appendItem with the correct
// context.
this.state = {
items: []
};
this.appendItem = this.appendItem.bind(this);
}
componentDidMount() {
// This line of code subscribes to the store,
// and allows you to provide a function to update
// the component state.
// store.subscribe returns a function to unsubscribe
// from the store. We'll use it later for when
// the component unmounts.
this.unsubscribe = store.subscribe(() => {
// In the subscribe function, get the application's
// state using store.getState().
// Set the component state's items array to the
// application's state items array.
const state = store.getState();
this.setState({
items: state.items
});
});
}
appendItem() {
// Dispatch the addItem action from inside the store.
// addItem takes an argument - the item that you
// want to pass into the store. Try passing a string.
//
// To dispatch the action, call store.dispatch and
// pass an action as the argument.
store.dispatch(addItem("Hello"));
}
render() {
const {items} = this.state;
const { items } = this.state;
return (
<div>
<ul>{items.map((value, index) => <li key={index}>{value}</li>)}</ul>
<ul>
{items.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<button onClick={this.appendItem}>Append item</button>
</div>
......@@ -51,8 +46,7 @@ class ConnectedComponent extends React.Component {
}
componentWillUnmount() {
// Call the function this.unsubscribe to remove
// the store subscription from the component.
this.unsubscribe();
}
}
......
......@@ -2,119 +2,75 @@ import React, { Component } from 'react';
import "./index.css"
import "./normalize.css"
import { connect } from 'react-redux'
import {
attemptWidgetCreation,
checkOrderForPackaging,
generateOrder,
orderMaterials,
shipOrder
} from './actions'
import Inventory from './Inventory'
import Materials from './Materials'
import Management from './Management'
import Manufacturing from './Manufacturing'
import Orders from './Orders'
import QA from './QA'
import Packaging from './Packaging'
import Shipping from './Shipping'
const Header = () => <img className="logo" src="/logo.png" />
const Error = (props) => <h4>{props.message}</h4>
class App extends Component {
constructor() {
super()
this.state = {
orders: [],
materials: {
dowel: { count: 2 },
screw: { count: 8 },
wheel: { count: 3 }
},
packaged: 0,
widgets: []
}
this.addWidget = this.addWidget.bind(this)
this.generateOrder = this.generateOrder.bind(this)
this.orderMaterials = this.orderMaterials.bind(this)
this.packageOrder = this.packageOrder.bind(this)
}
// The addWidget function removes widgets from materials
// and creates a new widget in state.
addWidget() {
const newMaterials = Object.assign({}, this.state.materials)
newMaterials.dowel.count -= 1
newMaterials.screw.count -= 2
newMaterials.wheel.count -= 2
// create a newWidget object, with the key created set to Date.now()
//
// Create a newWidgetsInventory array, which is an empty array
// with this.state.widgets and newWidget concatted onto it.
// Update state with materials set to newMaterials,
// and widgets set to newWidgets.
}
// generateOrder creates a new order with a random
// number of widgets needed to fulfill that order
generateOrder() {
const newOrder = {
created: Date.now(),
widgets: Math.floor(Math.random() * 10) + 1
}
// Create the newOrders variable, which is an empty array
// with this.state.orders and newOrder concatted onto it.
// Update the orders array in state to newOrders.
}
// orderMaterials adds 10 of each material to state
orderMaterials() {
const { materials } = this.state
const { dowel, screw, wheel } = materials
this.setState(Object.assign({}, this.state, {
materials: {
dowel: { count: dowel.count + 10 },
screw: { count: screw.count + 10 },
wheel: { count: wheel.count + 10 }
}
}))
}
// packageOrder removes the top order from state and,
// given that we have enough widgets to satisfy
// the amount requested in the order, adds 1 to "packaged"
// (as well as removes the top order from state, and
// subtracting the used widgets).
packageOrder() {
const { orders, packaged, widgets } = this.state
const newOrders = [].concat(orders)
// Get the top order from the array newOrders by
// calling .shift(). Assign it to the variable order.
// We should ensure that we have enough widgets to package
// this order. Check `if (order.widgets <= widgets.length)`...
// If it's true...
// Set state:
// - orders should be newOrders
// - packaged should be incremented
// - widgets should be (widgets - order.widgets)
}
render() {
const { error, materials, orders, packaged, widgets } = this.state
const {
attemptWidgetCreation,
checkOrderForPackaging,
error,
failed,
generateOrder,
materials,
orderMaterials,
orders,
packaged,
shipped,
shipOrder,
widgets
} = this.props
return (
<div>
<Header />
<Orders generateOrder={this.generateOrder} orders={orders} />
{error ? <Error message={error} /> : null}
<Orders generateOrder={generateOrder} orders={orders} />
<Materials materials={materials} />
<Manufacturing
addWidget={this.addWidget}
addWidget={attemptWidgetCreation}
materials={materials}
/>
<QA failed={failed} />
<Inventory widgets={widgets} />
<Packaging orders={orders} packaged={packaged} packageOrder={this.packageOrder} />
<Management orderMaterials={this.orderMaterials} />
<Packaging orders={orders} packaged={packaged} packageOrder={checkOrderForPackaging} />
<Shipping packaged={packaged} shipped={shipped} shipOrder={shipOrder} />
<Management orderMaterials={orderMaterials} />
</div>
);
}
}
export default App;
const mapStateToProps = (state) => state
const mapActionCreators = {
attemptWidgetCreation,
checkOrderForPackaging,
generateOrder,
orderMaterials,
shipOrder
}
export default connect(
mapStateToProps,
mapActionCreators
)(App);
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
import React, { Component } from 'react';
class Manufacturing extends Component {
constructor() {
super()
this.manufactureWidget = this.manufactureWidget.bind(this)
}
manufactureWidget() {
const { addWidget, materials } = this.props
if (materials.dowel.count >= 1 && materials.screw.count >= 2 && materials.wheel.count >= 2) {
addWidget()
}
}
render() {
return (
<div>
<h2>Manufacturing</h2>
<button onClick={this.manufactureWidget}>Manufacture widget</button>
<button onClick={this.props.addWidget}>Manufacture widget</button>
</div>
);
}
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
import React, { Component } from 'react';
class Shipping extends Component {
render() {
const { packaged, shipped, shipOrder } = this.props
return (
<div>
<h2>Shipping</h2>
<h4>{shipped} orders shipped</h4>
{packaged ? <button onClick={shipOrder}>Ship order</button> : <h4>No orders to ship</h4>}
</div>
);
}
}
export default Shipping;
export const GENERATE_ORDER = 'GENERATE_ORDER';
// "Simple" action creators:
// export const mySimpleAction = (optionalArg) => { return {
// type: ACTION_CONSTANT,
// optionalArg
// }}
// "Thunked" actions:
// export const myThunkedAction = () => {
// return (dispatch, getState) => {
// dispatch(someAction())
//
// if (someCondition) {
// dispatch(anotherThing(getState().somethingInState))
// }
// }
// }
import {
ADD_WIDGET,
GENERATE_ORDER,
ORDER_MATERIALS,
PACKAGE_ORDER,
PRESENT_ERROR,
SHIP_ORDER,
DOWELS_NEEDED,
SCREWS_NEEDED,
WHEELS_NEEDED
} from './constants'
export function attemptWidgetCreation() {
return (dispatch, getState) => {
const { materials } = getState()
const { dowel, screw, wheel } = materials
if (dowel.count >= DOWELS_NEEDED && screw.count >= SCREWS_NEEDED && wheel.count >= WHEELS_NEEDED) {
dispatch(addWidget())
} else {
dispatch(presentError("Not enough materials to create a widget"))
}
}
}
export function addWidget() {
return {
date: Date.now(),
type: ADD_WIDGET
}
}
export function generateOrder() {
return {
date: Date.now(),
type: GENERATE_ORDER
}
}
export function orderMaterials() {
return {
type: ORDER_MATERIALS
}
}
export function checkOrderForPackaging() {
return (dispatch, getState) => {
const { orders, widgets } = getState()
const order = orders[0]
if (order.widgets <= widgets.length) {
dispatch(packageOrder(order))
} else {
dispatch(presentError("Not enough widgets to fill order!"))
}
}
}
export function packageOrder(order) {
return {
type: PACKAGE_ORDER,
order
}
}
export function shipOrder() {
return {
type: SHIP_ORDER
}
}
export function presentError(message) {
return {
type: PRESENT_ERROR,
message
}
}
export default {
attemptWidgetCreation,
generateOrder,
orderMaterials,
packageOrder,
presentError,
shipOrder
}
export const ADD_WIDGET = 'ADD_WIDGET'
export const GENERATE_ORDER = 'GENERATE_ORDER'
export const ORDER_MATERIALS = 'ORDER_MATERIALS'
export const PACKAGE_ORDER = 'PACKAGE_ORDER'
export const PRESENT_ERROR = 'PRESENT_ERROR'
export const SHIP_ORDER = 'SHIP_ORDER'
export const DOWELS_NEEDED = 1
export const SCREWS_NEEDED = 2
export const WHEELS_NEEDED = 2
File mode changed from 100644 to 100755
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reduxLogger from 'redux-logger';
import reducer from './reducer';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(reducer, applyMiddleware(reduxLogger));
import appState from './reducers';
import App from './App';
import './index.css';
const store = createStore(
appState,
applyMiddleware(thunk)
);
ReactDOM.render(
<App />,
<Provider store={store}>
<App />
</Provider>
,
document.getElementById('root')
);
File mode changed from 100644 to 100755
const initialState = {
}
export default function appReducer(state = initialState, action) {
switch (action.type) {
default:
return state;
}
}
import {
ADD_WIDGET,
GENERATE_ORDER,
ORDER_MATERIALS,
PACKAGE_ORDER,
PRESENT_ERROR,
SHIP_ORDER,
DOWELS_NEEDED,
SCREWS_NEEDED,
WHEELS_NEEDED
} from './constants'
const initialState = {
error: null,
failed: 0,
orders: [],
materials: {
dowel: { count: 2 },
screw: { count: 8 },
wheel: { count: 3 }
},
packaged: 0,
shipped: 0,
widgets: []
}
const addWidget = (state, date) => {
const newMaterials = state.materials
newMaterials.dowel.count -= DOWELS_NEEDED
newMaterials.screw.count -= SCREWS_NEEDED