Skip to content

Commit b00ca2d

Browse files
committed
Port stock-ticker scenario
1 parent f78d8ea commit b00ca2d

File tree

7 files changed

+298
-0
lines changed

7 files changed

+298
-0
lines changed

src/scenarios/stock-ticker/App.jsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from "react";
2+
import { connect } from "react-redux";
3+
4+
import Slice from "./Slice";
5+
import { updateRandomPairInSlice } from "./pairActions";
6+
7+
let slices;
8+
9+
const mapState = state => {
10+
if (!slices) {
11+
slices = Array(Object.keys(state).length).fill(0);
12+
}
13+
14+
return { slices };
15+
};
16+
17+
const mapDispatch = { updateRandomPairInSlice };
18+
19+
class App extends React.Component {
20+
render() {
21+
return (
22+
<div>
23+
<button onClick={this.props.updateRandomPairInSlice}>
24+
Update Random Pair
25+
</button>
26+
<div className="row">
27+
{this.props.slices.map((slice, idx) => {
28+
return (
29+
<div className="col-lg-4" key={idx}>
30+
<Slice idx={idx} />
31+
</div>
32+
);
33+
})}
34+
</div>
35+
</div>
36+
);
37+
}
38+
}
39+
App.displayName = "App";
40+
41+
export default connect(
42+
mapState,
43+
mapDispatch
44+
)(App);

src/scenarios/stock-ticker/Pair.jsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from "react";
2+
import { connect } from "react-redux";
3+
4+
const mapState = (state, props) => state[props.sliceId][props.pairId];
5+
6+
class Pair extends React.Component {
7+
state = {
8+
direction: "up",
9+
value: this.props.value
10+
};
11+
12+
static getDerivedStateFromProps(props, state) {
13+
if (props.value === state.value) return null;
14+
15+
const direction = props.value > state.value ? "up" : "down";
16+
17+
return {
18+
value: props.value,
19+
direction
20+
};
21+
}
22+
23+
shouldComponentUpdate(nextProps) {
24+
return this.props.value !== nextProps.value;
25+
}
26+
27+
render() {
28+
const { direction } = this.state;
29+
30+
return (
31+
<li className="list-group-item">
32+
<span>{this.props.name}</span>
33+
<span
34+
className={
35+
"pull-right " +
36+
(direction === "up" ? "text-success" : "text-warning")
37+
}
38+
>
39+
<span
40+
className={
41+
"glyphicon " +
42+
(direction === "up"
43+
? "glyphicon-arrow-up"
44+
: "glyphicon-arrow-down")
45+
}
46+
/>
47+
<span>{this.props.value}</span>
48+
</span>
49+
</li>
50+
);
51+
}
52+
}
53+
Pair.displayName = "Pair";
54+
55+
export default connect(mapState)(Pair);

src/scenarios/stock-ticker/Slice.jsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React, { Component } from "react";
2+
import { connect } from "react-redux";
3+
4+
import Pair from "./Pair";
5+
import { fillPairs } from "./pairActions";
6+
7+
const actions = { fillPairs };
8+
9+
const mapStateToProps = (state, props) => {
10+
return {
11+
slice: state[props.idx]
12+
};
13+
};
14+
15+
class Slice extends Component {
16+
state = {};
17+
18+
componentDidMount = () => {
19+
//this.props.fillPairs(this.props.idx);
20+
};
21+
22+
render() {
23+
const { slice, idx } = this.props;
24+
return (
25+
<ul className="list-group">
26+
{slice.map(pair => {
27+
return <Pair key={pair.id} sliceId={idx} pairId={pair.id} />;
28+
})}
29+
</ul>
30+
);
31+
}
32+
}
33+
Slice.displayName = "Slice";
34+
35+
export default connect(
36+
mapStateToProps,
37+
actions
38+
)(Slice);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const FILL_PAIRS = "fill-pairs";
2+
export const UPDATE_PAIR = "update-pair";
3+
4+
export const NUMBER_OF_SLICES = 4;
5+
export const NUM_ENTRIES = 3500;

src/scenarios/stock-ticker/index.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React, { useLayoutEffect } from 'react'
2+
import { configureStore, AnyAction } from '@reduxjs/toolkit'
3+
4+
import { renderApp } from '../../common'
5+
6+
import App from './App'
7+
import * as c from './constants'
8+
import { updatePair, updateRandomPairInSlice, fillPairs } from './pairActions'
9+
10+
const initialState: any = []
11+
12+
const highOrderSliceReducer =
13+
(sliceId = '') =>
14+
(state = initialState, action: AnyAction) => {
15+
switch (action.type) {
16+
case `${c.FILL_PAIRS}_${sliceId}`: {
17+
return [...action.pairs]
18+
}
19+
case `${c.UPDATE_PAIR}_${sliceId}`: {
20+
return state.map((pair: any) => {
21+
return pair.id === action.id ? { ...pair, value: action.value } : pair
22+
})
23+
}
24+
default: {
25+
return state
26+
}
27+
}
28+
}
29+
30+
const reducers = Array(c.NUMBER_OF_SLICES)
31+
.fill(0)
32+
.reduce((acc, curr, i) => {
33+
acc[i] = highOrderSliceReducer(i.toString())
34+
return acc
35+
}, {})
36+
37+
const store = configureStore({
38+
reducer: reducers,
39+
middleware: (gdm) =>
40+
gdm({
41+
immutabilityCheck: false,
42+
serializableCheck: false,
43+
}),
44+
})
45+
46+
for (let i = 0; i < c.NUMBER_OF_SLICES; i++) {
47+
// @ts-ignore
48+
store.dispatch(fillPairs(i))
49+
}
50+
51+
function addTweetInRandomSlice() {
52+
const sliceId = Math.floor(Math.random() * c.NUMBER_OF_SLICES)
53+
// @ts-ignore
54+
store.dispatch(addTweet(sliceId))
55+
}
56+
57+
function doRandomUpdate() {
58+
store.dispatch(updateRandomPairInSlice())
59+
}
60+
61+
const StockTickerApp = () => {
62+
useLayoutEffect(() => {
63+
setInterval(doRandomUpdate, 13)
64+
65+
setInterval(doRandomUpdate, 21)
66+
67+
setInterval(doRandomUpdate, 34)
68+
69+
setInterval(doRandomUpdate, 55)
70+
}, [])
71+
72+
return <App />
73+
}
74+
75+
// @ts-ignore
76+
renderApp(StockTickerApp, store)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Chance from "chance";
2+
import * as c from "./constants";
3+
4+
const chance = new Chance();
5+
6+
function createPairs() {
7+
const pairs = [];
8+
const entries = Math.floor(c.NUM_ENTRIES / c.NUMBER_OF_SLICES);
9+
for (let i = 0; i < entries; i++) {
10+
const pair = chance.currency_pair();
11+
pairs.push({
12+
id: i,
13+
value: Math.random(),
14+
name: pair[0].code + pair[1].code
15+
});
16+
}
17+
return pairs;
18+
}
19+
20+
export function fillPairs(id) {
21+
return {
22+
type: `${c.FILL_PAIRS}_${id}`,
23+
pairs: createPairs()
24+
};
25+
}
26+
27+
// function getRandIndex () {
28+
// return Math.floor(Math.random() * Math.floor(c.NUM_ENTRIES / c.NUMBER_OF_SLICES))
29+
// }
30+
31+
export function updatePair(sliceId, pairId) {
32+
//console.log(sliceId, pairId);
33+
return {
34+
type: `${c.UPDATE_PAIR}_${sliceId}`,
35+
id: pairId,
36+
value: Math.random()
37+
};
38+
}
39+
40+
export function updateRandomPairInSlice() {
41+
return (dispatch, getState) => {
42+
const pairId = Math.floor(
43+
Math.random() * (c.NUM_ENTRIES / c.NUMBER_OF_SLICES)
44+
);
45+
const sliceId = Math.floor(Math.random() * c.NUMBER_OF_SLICES);
46+
dispatch(updatePair(sliceId, pairId));
47+
};
48+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { combineReducers } from "redux";
2+
import * as c from "./constants";
3+
4+
const initialState = [];
5+
6+
const highOrderSliceReducer = (sliceId = "") => (
7+
state = initialState,
8+
action
9+
) => {
10+
switch (action.type) {
11+
case `${c.FILL_PAIRS}_${sliceId}`: {
12+
return [...action.pairs];
13+
}
14+
case `${c.UPDATE_PAIR}_${sliceId}`: {
15+
return state.map(pair => {
16+
return pair.id === action.id ? { ...pair, value: action.value } : pair;
17+
});
18+
}
19+
default: {
20+
return state;
21+
}
22+
}
23+
};
24+
25+
const reducers = Array(c.NUMBER_OF_SLICES)
26+
.fill(0)
27+
.reduce((acc, curr, i) => {
28+
acc[i] = highOrderSliceReducer(i);
29+
return acc;
30+
}, {});
31+
32+
export default combineReducers(reducers);

0 commit comments

Comments
 (0)