React hooks
This commit is contained in:
parent
d545fbe636
commit
abdda1e8d0
7 changed files with 169 additions and 168 deletions
|
@ -41,20 +41,18 @@ router.get('/twitter', (req, res) => {
|
|||
|
||||
// MARK: - /stream
|
||||
router.get('/stream', (req, res) => {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
})
|
||||
|
||||
streamClient.stream('statuses/filter', {track: req.query.hashtag}, function(stream) {
|
||||
stream.on('data', function(tweet) {
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: tweets
|
||||
})
|
||||
// console.log(tweet.text)
|
||||
res.write(JSON.stringify(tweet, 0, 2))
|
||||
})
|
||||
|
||||
stream.on('error', function(error) {
|
||||
console.log('Error ' + JSON.stringify(error, 0, 2))
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error
|
||||
})
|
||||
console.log(error)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,25 +3,25 @@ import List from './List'
|
|||
import Form from './Form'
|
||||
import Post from './Posts'
|
||||
|
||||
const App = () => (
|
||||
<div className = 'row mt-5'>
|
||||
export default function App() {
|
||||
return (
|
||||
<div className = 'row mt-5'>
|
||||
|
||||
<div className = 'col-md-4 offset-md-1'>
|
||||
<h2>Articles</h2>
|
||||
<List />
|
||||
</div>
|
||||
<div className = 'col-md-4 offset-md-1'>
|
||||
<h2>Articles</h2>
|
||||
<List />
|
||||
</div>
|
||||
|
||||
<div className = 'col-md-4 offset-md-1'>
|
||||
<h2>Add a new article</h2>
|
||||
<Form />
|
||||
</div>
|
||||
<div className = 'col-md-4 offset-md-1'>
|
||||
<h2>Add a new article</h2>
|
||||
<Form />
|
||||
</div>
|
||||
|
||||
<div className="col-md-4 offset-md-1">
|
||||
<h2>Latest tweets</h2>
|
||||
<Post />
|
||||
</div>
|
||||
|
||||
<div className="col-md-4 offset-md-1">
|
||||
<h2>Latest tweets</h2>
|
||||
<Post />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
export default App
|
||||
)
|
||||
}
|
||||
|
|
7
src/js/components/Form.css
Normal file
7
src/js/components/Form.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
button {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.clicked {
|
||||
background-color: lightgray;
|
||||
}
|
|
@ -1,9 +1,48 @@
|
|||
import React, { Component } from "react"
|
||||
// MARK: Definitions
|
||||
import React, { useState, useEffect } from "react"
|
||||
import { connect } from "react-redux"
|
||||
import uuidv1 from "uuid"
|
||||
import { addArticle } from "../actions/index"
|
||||
import { setCandidate } from "../actions/index"
|
||||
import './Form.css'
|
||||
|
||||
export function ConnectedForm(props) {
|
||||
// MARK: State
|
||||
let [selected, setSelected] = useState('')
|
||||
let { candidate, setCandidate } = props
|
||||
|
||||
// MARK: Effects
|
||||
// MARK: - Set Clinton as initial candidate
|
||||
useEffect(() => {
|
||||
setCandidate('Hillary Clinton')
|
||||
setSelected('Hillary Clinton')
|
||||
}, [setCandidate])
|
||||
|
||||
// MARK: - Set candidate when user clicks on button
|
||||
useEffect(() => {
|
||||
setCandidate(selected)
|
||||
})
|
||||
|
||||
// MARK: Actions
|
||||
let clickTrump = () => {
|
||||
setSelected('Donald Trump')
|
||||
}
|
||||
|
||||
let clickHillary = () => {
|
||||
setSelected('Hillary Clinton')
|
||||
}
|
||||
|
||||
// MARK: Return
|
||||
return (
|
||||
<div className='form'>
|
||||
<button className={selected === 'Donald Trump' ? 'clicked' : null} onClick={clickTrump}>Donald Trump</button>
|
||||
<button className={selected === 'Hillary Clinton' ? 'clicked' : null} onClick={clickHillary}>Hillary Clinton</button>
|
||||
|
||||
<p>{candidate}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: Redux
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
addArticle: article => dispatch(addArticle(article)),
|
||||
|
@ -11,80 +50,6 @@ function mapDispatchToProps(dispatch) {
|
|||
}
|
||||
}
|
||||
|
||||
class ConnectedForm extends Component {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.state = {
|
||||
title: '',
|
||||
candidate: ''
|
||||
}
|
||||
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.clickTrump = this.clickTrump.bind(this)
|
||||
this.clickHilary = this.clickHilary.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.setCandidate('Hillary Clinton')
|
||||
this.setState({ candidate: 'Hillary Clinton'})
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
this.setState({ [event.target.id]: event.target.value })
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
const { title } = this.state
|
||||
const id = uuidv1()
|
||||
|
||||
this.props.addArticle({ title, id })
|
||||
this.setState({ title: "" })
|
||||
}
|
||||
|
||||
clickTrump(event) {
|
||||
event.preventDefault()
|
||||
this.props.setCandidate('Donald Trump')
|
||||
this.setState({ candidate: 'Donald Trump' })
|
||||
}
|
||||
|
||||
clickHilary(event) {
|
||||
event.preventDefault()
|
||||
this.props.setCandidate('Hillary Clinton')
|
||||
this.setState({ candidate: 'Hillary Clinton' })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title } = this.state
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor="title">Title</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="btn btn-success btn-lg">
|
||||
SAVE
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<button onClick={this.clickTrump}>Donald Trump</button>
|
||||
<button onClick={this.clickHilary}>Hillary Clinton</button>
|
||||
|
||||
<p>{this.state.candidate}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Form = connect(null, mapDispatchToProps)(ConnectedForm)
|
||||
|
||||
export default Form
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
import React, { Component } from "react"
|
||||
import React, { useEffect } from "react"
|
||||
import { connect } from "react-redux"
|
||||
import { getHillaryData } from "../actions/index"
|
||||
import { getTrumpData } from "../actions/index"
|
||||
|
||||
export class Post extends Component {
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
if (nextProps.candidate !== this.props.candidate) {
|
||||
if (nextProps.candidate === 'Hillary Clinton') {
|
||||
this.props.getHillaryData()
|
||||
}
|
||||
else if (nextProps.candidate === 'Donald Trump'){
|
||||
this.props.getTrumpData()
|
||||
}
|
||||
export function Post(props) {
|
||||
let { articles, candidate, getHillaryData, getTrumpData } = props
|
||||
|
||||
// Fetch tweets when user clicks on candidate's button
|
||||
useEffect(() => {
|
||||
if (candidate === 'Hillary Clinton') {
|
||||
getHillaryData()
|
||||
}
|
||||
}
|
||||
else if (candidate === 'Donald Trump') {
|
||||
getTrumpData()
|
||||
}
|
||||
}, [candidate, getHillaryData, getTrumpData])
|
||||
|
||||
render() {
|
||||
let cand = this.props.candidate === undefined ? 'Not set' : this.props.candidate
|
||||
let cand = candidate === undefined ? 'Not set' : candidate
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>@{cand}</p>
|
||||
<ul className="list-group list-group-flush">
|
||||
{this.props.articles.map(el => (
|
||||
<li className="list-group-item" key={el.id_str}>
|
||||
<p>{el.created_at}</p>
|
||||
<p>{el.text}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<p>@{cand}</p>
|
||||
<ul className="list-group list-group-flush">
|
||||
{articles.map(el => (
|
||||
<li className="list-group-item" key={el.id_str}>
|
||||
<p>{el.created_at}</p>
|
||||
<p>{el.text}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
|
@ -23,12 +23,20 @@ function rootReducer(state = initialState, action) {
|
|||
}
|
||||
|
||||
else if (action.type === DATA_LOADED) {
|
||||
console.log('Payload: ' + action.payload)
|
||||
return Object.assign({}, state, {
|
||||
remoteArticles: action.payload.message.statuses
|
||||
})
|
||||
}
|
||||
|
||||
else if (action.type === DATA_LOADED_TO_ADD) {
|
||||
console.log('Payload: ' + action.payload)
|
||||
return Object.assign({}, state, {
|
||||
remoteArticles: state.remoteArticles.concat(action.payload)
|
||||
})
|
||||
}
|
||||
|
||||
else if (action.type === "DATA_LOADED_TO_ADD_2") {
|
||||
return Object.assign({}, state, {
|
||||
remoteArticles: state.remoteArticles.concat(action.payload.message.statuses)
|
||||
})
|
||||
|
|
|
@ -1,69 +1,93 @@
|
|||
import { takeEvery, call, put, all } from 'redux-saga/effects'
|
||||
import { takeEvery, call, put, all/*, take*/ } from 'redux-saga/effects'
|
||||
// import { io, eventChannel } from 'redux-saga'
|
||||
|
||||
export default function* watcherSaga() {
|
||||
yield all([
|
||||
worker(),
|
||||
workerHillary(),
|
||||
workerTrump()
|
||||
])
|
||||
}
|
||||
|
||||
// Data worker
|
||||
function* worker() {
|
||||
yield takeEvery("DATA_REQUESTED", workerSaga)
|
||||
}
|
||||
|
||||
function* workerSaga() {
|
||||
try {
|
||||
const payload = yield call(getData)
|
||||
yield put({ type: "DATA_LOADED", payload })
|
||||
} catch (e) {
|
||||
yield put({ type: "API_ERRORED", payload: e })
|
||||
}
|
||||
}
|
||||
|
||||
function getData() {
|
||||
return fetch("https://jsonplaceholder.typicode.com/posts").then(response =>
|
||||
response.json()
|
||||
)
|
||||
}
|
||||
|
||||
// Hillary worker
|
||||
function* workerHillary() {
|
||||
yield takeEvery("DATA_HILLARY_REQUESTED", workerHillarySaga)
|
||||
yield takeEvery('DATA_HILLARY_REQUESTED', workerHillarySaga)
|
||||
}
|
||||
|
||||
function* workerHillarySaga() {
|
||||
try {
|
||||
const payload = yield call(getHillaryData)
|
||||
yield put({ type: "DATA_LOADED", payload })
|
||||
yield put({ type: 'DATA_LOADED', payload })
|
||||
} catch (e) {
|
||||
yield put({ type: "API_ERRORED", payload: e })
|
||||
yield put({ type: 'API_ERRORED', payload: e })
|
||||
}
|
||||
}
|
||||
|
||||
function getHillaryData() {
|
||||
return fetch("http://localhost:3030/api/twitter?hashtag=Hillary%20Clinton").then(response =>
|
||||
response.json()
|
||||
)
|
||||
return fetch('http://localhost:3030/api/twitter?hashtag=Hillary%20Clinton')
|
||||
.then(response => response.json())
|
||||
}
|
||||
|
||||
// Trump worker
|
||||
function* workerTrump() {
|
||||
yield takeEvery("DATA_TRUMP_REQUESTED", workerTrumpSaga)
|
||||
yield takeEvery('DATA_TRUMP_REQUESTED', workerTrumpSaga) // listenServerSaga
|
||||
}
|
||||
|
||||
function* workerTrumpSaga() {
|
||||
try {
|
||||
const payload = yield call(getTrumpData)
|
||||
yield put({ type: "DATA_LOADED", payload })
|
||||
yield put({ type: 'DATA_LOADED', payload })
|
||||
} catch (e) {
|
||||
yield put({ type: "API_ERRORED", payload: e })
|
||||
yield put({ type: 'API_ERRORED', payload: e })
|
||||
}
|
||||
}
|
||||
|
||||
function getTrumpData() {
|
||||
return fetch("http://localhost:3030/api/twitter?hashtag=Donald%20Trump").then(response =>
|
||||
response.json()
|
||||
)
|
||||
return fetch('http://localhost:3030/api/twitter?hashtag=Donald%20Trump')
|
||||
.then(response => response.json())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ---------- Stream ----------------
|
||||
/*
|
||||
const socketServerURL = 'http://localhost:3030/api/stream?hashtag=space'
|
||||
let socket;
|
||||
|
||||
wrapping function for socket.on
|
||||
const connect = () => {
|
||||
socket = io(socketServerURL);
|
||||
return new Promise((resolve) => {
|
||||
socket.on('connect', () => {
|
||||
resolve(socket);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// This is how a channel is created
|
||||
const createSocketChannel = socket => eventChannel((emit) => {
|
||||
const handler = (data) => {
|
||||
emit(data);
|
||||
};
|
||||
socket.on('newTask', handler);
|
||||
return () => {
|
||||
socket.off('newTask', handler);
|
||||
};
|
||||
});
|
||||
|
||||
// saga that listens to the socket and puts the new data into the reducer
|
||||
const listenServerSaga = function* () {
|
||||
// connect to the server
|
||||
const socket = yield call(connect);
|
||||
|
||||
// then create a socket channel
|
||||
const socketChannel = yield call(createSocketChannel, socket);
|
||||
|
||||
// then put the new data into the reducer
|
||||
while (true) {
|
||||
const payload = yield take(socketChannel);
|
||||
yield put({type: 'DATA_LOADED', payload});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue