A simplified clone of the Twitch web application. Twitch is a video streaming web application with a focus on the gaming community. Because this app does not need a backend, this project allowed me to further develop my React/Redux, React Router, CRUD, authentication, and error handling skills.
This app is different from Twitch. With Twitch every user has one stream/channel that they can stream to. In this app, every user can create unlimited channels/streams that they can stream to.
This README includes my thought process and skills learned throughout the project.
This is an ongoing project.
npm install npm start
Create-React-App
React-Router-Dom
Redux
React-Redux
redux-devtools-extension
redux-form npm install redux-form@8.1.0
json-server
Stephen Grider
- Need to be able to navigate around to separate pages in out app
- Solution - React Router
- Need to allow a user to log in and out (authentication)
- Solution - Google OAuth
- Need to hand forms in Redux
- Solution - practice
- Need to master CRUD operations in React/Redux
- Create, Read, Update, Destroy
- Errors will occur! Need good error handling
- User errors
- Create Stream name already exists
- Search for stream does not exist
- Failed load of video
- User errors
- Streamer’s Computer records video with Open Broadcaster Software (OBS) →
- Video stream + stream key →
- API - Real Time Messaging Protocol (RTMP) Server →
- Separate Web Server that knows which streams are currently broadcasting →
- Broadcast Video Feed → Multiple Viewers’ Browser →
- Viewers request to get video from RTMP → viewer…
- Not logged in
- User can view a list of all stream/channels
- User can view video for a single stream
- Logged in
- User can create a new stream/channel
- User can edit a stream/channel they have created
- User can delete a stream/channel they have created
- Streamer (index page)
- Streams (stream page)
- Login/Logout
- Streams Header
- Card - Someone elses Stream + description
- Card - My Stream + description
- Delete button
- Route to Delete stream page
- Card - Delete Stream
- “Are you sure you want to delete this stream?”
- Button - Delete
- Button - Cancel
- Card - Delete Stream
- Route to Delete stream page
- Edit button
- Route to Edit Stream Page
- Form - Edit a Stream
- Input - Title
- Input - Description
- Submit button
- Route to Edit Stream Page
- Delete button
- Create Stream button
- Route to Create Stream Page
- Form - Create Stream
- Input - Title
- Input - Description
- Submit button
- Route to Create Stream Page
- react-router
- core navigation lib - this is not installed manually
- react-router-dom
- Navigation for dom-based apps
- react-router-native
- navigation for react native apps
- react-router-redux
- bindings with Redux and React Router (not necessary for most apps)
- BrowserRouter
- uses everything after the TLD or port as the 'path
- HashRouter
- uses everything after the
#
as the 'path'
- uses everything after the
- MemoryRouter
- Doesn't use the URL to track navigation
- history - keeps track of the address bar in your browser -->
<BrowserRouter/>
- listens to 'history' for changes in URL --><Route path="" component={}/>
- path = "/" exact || "/path" || "path/path/..."
- first page the app should load
- component = { componentNameToLoad }
- path = "/" exact || "/path" || "path/path/..."
- put href in anchor tags
- Bad navigation:
- You add an
<a/>
tag to app with href='/' and click it - your browser maked a request to localhost:3000
- development server responds iwth index.html file
- PROBLEM: Browser receives index.html file, dumps old HTML file it is showing (incl all of React/Redux state data!)
- index.html file lists JS files in sript tags - browser downloads and executes these scipts
- App starts up
- You add an
- SOLUTION - use
<Link to=""/>
instead- User wants to navigate to another page in app
- user click 'Link' tag
- GOOD: React Router prevents the browser from navigating to the new page and fetching new index.html file!
- URL still changes
- 'History' sees updated URL, takes URL and sends it to
BrowserRouter
BrowserRouter
communicates URL toRoute
componentsRoute
components rerender to show new set of components
- Create components
- import to App
- Create Routes within BrowserRouter:
const App = () => {
return (
<div>
<BrowserRouter>
<div>
<Route path='/' exact component={component1}/>
<Route path='/folder1/name1' component={component2} />
<Route path='/folder1/name2' component={component3} />
</div>
</BrowserRouter>
</div>
);
}
- Test routes in browser by manually entering routes in browser input
Put the component inside the App component -- inside BrowserRouter if it has <Link/>
in it
- Store a record in a database with the user's email and password
- When the user tries to login, compare email.ps with what is stored in database
- A user is "logged in" when they enter the correct email/pw
- Results in a 'token' that the server can use to make requests on behalf of the user
- Usually used when an app needs to access user data when they are not logged in
- Difficult to setup because a lot of information about the user needs to be stored
- User authenitcates with outside service provider (Google, Linkedin, Facebook, etc.)
- User authorizes the app to access their information
- Outside provider tells us about the user
- Use a provider that is trusted to correctly handle identification of a user
- OAuth can be used for:
- user identification in the app
- the app making action on the behalf of the user
- API Scopes - limit the amount of information OAuth has on behalf of the user
- Results in a 'token' that a browser app can use to make requests on behalf of the user
- Usually used when an app only needs access to user data while they are logged in
- Very easy to set up because of automated flow
- Create a new project at
console.developers.google.com/
- Set up an OAuth confirmation screen
- Generate an OAuth Client ID
- Install Google's API library, initialize it with the OAuth Client ID
- add to in index.html :
<script src="https://apis.google.com/js/api.js"></script>
- gives access to gapi
- add to in index.html :
- Make sure the lib gets called any time the user clicks on the 'Login with Google' button
- Create Redux folders/files
- src/actions/index.js
- src/reducers/index.js
- import {combineReducers} from 'redux'; export default combineReducers({ replaceMe: ()=> 'xyz'})
- update src/index.js
- Provider form react-redux
- createStore from redux
- BEST PRACTICE:src > actions > types.js ==> Put all types of action names here to preven typo errors
- Root index.js - add this:
// add applyMiddleware, compose to import
import {createStore, applyMiddleware, compose} from 'redux';
...
// set const after imports
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
...
// add this to your store function; after reducers
composeEnhancers(applyMiddleware())
- The circle icon at the top for the GUI
localhost:XXXX/?debug_session=logged_in=yourNameOfChoiceHere
will save your info in GUI between reloads
docs -- redux-form is TRICKYYY this app uses the: Synchronous Application Form
- the maintainer made an oopsie and rolled this back to an out dated version
- use
npm install redux-form@8.1.0
to install the latest known good release - reducers > index.js add:
import { reducer as formReducer } from 'redux-form';
combineReducers({...form: formReducer})
- components > StreamCreator
import { Field, reduxForm } from 'redux-form';
- change to class StreamCreator
- see code for
- add reduxForm to export
- create
- create renderInput()
- create onSubmit()
- BEST PRACTICE: Form Validation; Security and Accuracy
Redux Form Reducer --> Redux Form mapStateToProps --> props -->
^ {DOM stuff handled by react}
^-----Redux Form Action Creator <-- handler <--
Naming Convention: GET, POST, PUT, DELETE (CRUD)
Actions | Method | Route |
---|---|---|
List all records | GET | /streams |
Get one particular record | GET | /streams/:id |
Create record | POST | /streams |
Update a record | PUT | /streams/:id |
Delete a record | DELETE | /streams/:id |