ref – https://stackoverflow.com/questions/3573623/how-do-i-preview-stash-contents-in-git
git stash show -p stash@{0}
To apply a specific stash:
git stash apply stash@{0}
ref – https://stackoverflow.com/questions/3573623/how-do-i-preview-stash-contents-in-git
git stash show -p stash@{0}
To apply a specific stash:
git stash apply stash@{0}
ref – https://codesandbox.io/s/audio-experiments-w7yz8?file=/src/App.js:0-2208
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
import React, { useState, useRef } from "react"; import "./styles.css"; export default function App() { const [stream, setStream] = useState({ access: false, recorder: null, error: "" }); const [recording, setRecording] = useState({ active: false, available: false, url: "" }); const chunks = useRef([]); function getAccess() { navigator.mediaDevices .getUserMedia({ audio: true }) .then((mic) => { let mediaRecorder; try { mediaRecorder = new MediaRecorder(mic, { mimeType: "audio/webm" }); } catch (err) { console.log(err); } const track = mediaRecorder.stream.getTracks()[0]; track.onended = () => console.log("ended"); mediaRecorder.onstart = function () { setRecording({ active: true, available: false, url: "" }); }; mediaRecorder.ondataavailable = function (e) { console.log("data available"); chunks.current.push(e.data); }; mediaRecorder.onstop = async function () { console.log("stopped"); const url = URL.createObjectURL(chunks.current[0]); chunks.current = []; setRecording({ active: false, available: true, url }); }; setStream({ ...stream, access: true, recorder: mediaRecorder }); }) .catch((error) => { console.log(error); setStream({ ...stream, error }); }); } return ( <div className="App"> {stream.access ? ( <div className="audio-container"> <button className={recording.active ? "active" : null} onClick={() => !recording.active && stream.recorder.start()} > Start Recording </button> <button onClick={() => stream.recorder.stop()}>Stop Recording</button> {recording.available && <audio controls src={recording.url} />} </div> ) : ( <button onClick={getAccess}>Get Mic Access</button> )} </div> ); } |
We start off on the frontend, where we submit some data.
frontend – src/pages/Register.jsx
1 2 3 4 5 |
const onSubmit = (e) => { // other code dispatch(register(userData)) // HERE } } |
we dispatch register with userData, via createAsyncThunk. createAsyncThunk creates an async request. It generates promise lifecycle action types pending/fulfilled/rejected.
We start the process by defining a payload creator, which is the anon function provided to createAsyncThunk. It tries to execute our authServer.register function.
frontend – src/features/auth/authSlice.js
1 2 3 4 5 6 7 |
export const register = createAsyncThunk( 'auth/register', async (user, thunkAPI) => { try { return await authService.register(user) // HERE } catch(...) { } ) |
This allows us to use axios’s post and hit the URL with the body data.
frontend – src/features/auth/authService.js
1 2 3 4 5 |
const register = async (userData) => { // other code const response = await axios.post(process.env.API_URL, userData) // HERE // other code } |
The request is received on the backend.
When we request to register a user, Node receives the request and data in the body.
If the username doesn’t exist, it would create the user, and then generate a token.
It would return the newly created user data and the generated token back to the front end.
backend – backend/controllers/userController.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
const registerUser = asyncHandler(async (req, res) => { const { username, password } = req.body // other code const user = await User.create({ username, password: hashedPassword, }) if (user) { res.status(201).json({ // HERE _id: user.id, username: user.username, token: generateToken(user._id), }) } } |
In order to generate the token, it would take the user’s id, the JWT_SECRET string, and create a token by using the sign function.
1 2 3 4 5 6 |
// Generate JWT const generateToken = (id) => { return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d', }) } |
JWTs can be signed using a secret (in our case JWT_SECRET string) with the HMAC algorithm.
It will spit back a long jwt token string.
This string can only be decrypted with the same string.
The reason why we do this is because we want the front end to use the same token and send it back to use, which needs to be decrypted by using the same JWT_SECRET:
backend/middleware/authMiddleware.js
1 |
const decoded = jwt.verify(token, process.env.JWT_SECRET) |
We can then get the user’s id and find it in our database. This ensures that the client making the request is someone we have dealt with before.
1 2 3 4 5 6 7 8 9 |
const register = async (userData) => { // FETCHED DATA RETURNS HERE const response = await axios.post(process.env.API_URL, userData) // other code return response.data // RETURNS THE FETCHED DATA } |
we return the data to the async anon function that is the 2nd parameter of createAsyncThunk:
1 2 3 4 5 6 7 8 |
export const register = createAsyncThunk( 'auth/register', async (user, thunkAPI) => { try { return await authService.register(user) // HERE } catch (error) { ...} } ) |
This will return to our state’s user object via action.payload when our register thunk’s promise has been fullfilled:
1 2 3 4 5 |
.addCase(register.fulfilled, (state, action) => { state.isLoading = false state.isSuccess = true state.user = action.payload }) |
This is how our thunkAPI’s can access the token from its user object in the state.
The thunk can access the store (getState) and accesses its reducer auth, which then accesses its state property user, and finally, the token property.
The user comes from the state:
And the auth is the reducer’s property name in the store:
ref- https://radixweb.com/blog/installing-npm-and-nodejs-on-windows-and-mac
ref – https://beta.reactjs.org/reference/react/lazy#suspense-for-code-splitting
Usually, you import components with the static import declaration:
1 |
import MarkdownPreview from './MarkdownPreview.js'; |
To defer loading this component’s code until it’s rendered for the first time, replace this import with:
1 2 |
import { lazy } from 'react'; const MarkdownPreview = lazy(() => import('./MarkdownPreview.js')); |
Now that your component’s code loads on demand, you also need to specify what should be displayed while it is loading. You can do this by wrapping the lazy component or any of its parents into a
1 2 3 4 |
<Suspense fallback={<Loading />}> <h2>Preview</h2> <MarkdownPreview /> </Suspense> |
the code for MarkdownPreview won’t be loaded until you attempt to render it. If MarkdownPreview hasn’t loaded yet, Loading will be shown in its place.
1 2 3 4 5 6 |
{showPreview && ( <Suspense fallback={<Loading />}> <h2>Preview</h2> <MarkdownPreview markdown={markdown} /> </Suspense> )} |
First let’s look at a function delayForDemo that will delay the the lazy loading of our MarkdownPreview.
The import() returns a Promise that will be fulfilled once the module is loaded completely.
Hence, our parameter of promise receives imported components.
We do a timeout of 2 seconds, and then return the promise right after.
1 2 3 4 5 6 |
// Add a fixed delay so you can see the loading state function delayForDemo(promise) { return new Promise(resolve => { setTimeout(resolve, 2000); }).then(() => promise); } |
We then use it on the import like so:
1 |
const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js'))); |
The whole point of this is to show that while an import component is being lazy loaded, we must show the fallback Loading component.
1 2 3 4 |
<Suspense fallback={<Loading />}> <h2>Preview</h2> <MarkdownPreview markdown={markdown} /> </Suspense> |
Notice
1 2 3 4 |
const [showPreview, setShowPreview] = useState(false); ... ... <input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} /> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import { useState, Suspense, lazy } from 'react'; import Loading from './Loading.js'; const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js'))); export default function MarkdownEditor() { const [showPreview, setShowPreview] = useState(false); const [markdown, setMarkdown] = useState('Hello, **world**!'); return ( <> <textarea value={markdown} onChange={e => setMarkdown(e.target.value)} /> <label> <input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} /> Show preview </label> <hr /> {showPreview && ( <Suspense fallback={<Loading />}> <h2>Preview</h2> <MarkdownPreview markdown={markdown} /> </Suspense> )} </> ); } // Add a fixed delay so you can see the loading state function delayForDemo(promise) { return new Promise(resolve => { setTimeout(resolve, 2000); }).then(() => promise); } |
ref – https://stackoverflow.com/questions/34570758/why-do-we-need-middleware-for-async-flow-in-redux
…for example, Why can’t the container component call the async API, and then dispatch the actions?
There is nothing wrong with this approach. It’s just inconvenient in a large application because you’ll have different components performing the same actions, you might want to:
So it is just easier from the maintenance point of view to extract action creators into separate functions.
Therefore, the benefit of using middleware like Redux Thunk or Redux Promise is that components aren’t aware of how action creators are implemented…
and whether they care about Redux state,
whether they are synchronous or asynchronous,
and whether or not they call other action creators.
The downside is a little bit of indirection, but we believe it’s worth it in real applications.
Using middleware – By using middle are for async management, component doesn’t care that the action creator is async. It just calls dispatch normally, it can also use mapDispatchToProps to bind such action creator with a short syntax, etc. The components don’t know how action creators are implemented, and you can switch between different async approaches (Redux Thunk, Redux Promise, Redux Saga) without changing the components.
Custom way (implement it by hand) – On the other hand, explicit approach, your components know exactly that a specific call is async, and needs dispatch to be passed by some convention (for example, as a sync parameter).
Also think about how this code will change. Say we want to have a second data loading function, and to combine them in a single action creator.
We need to be mindful of what kind of action creator we are calling:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// action creators function loadSomeData(dispatch, userId) { return fetch(`http://data.com/${userId}`) .then(res => res.json()) .then( data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }), err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err }) ); } function loadOtherData(dispatch, userId) { return fetch(`http://data.com/${userId}`) .then(res => res.json()) .then( data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }), err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err }) ); } function loadAllData(dispatch, userId) { return Promise.all( loadSomeData(dispatch, userId), // pass dispatch first: it's async loadOtherData(dispatch, userId) // pass dispatch first: it's async ); } // component componentWillMount() { loadAllData(this.props.dispatch, this.props.userId); // pass dispatch first } |
With Redux Thunk action creators can dispatch the result of other action creators and not even think whether those are synchronous or asynchronous:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// action creators function loadSomeData(userId) { return dispatch => fetch(`http://data.com/${userId}`) .then(res => res.json()) .then( data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }), err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err }) ); } function loadOtherData(userId) { return dispatch => fetch(`http://data.com/${userId}`) .then(res => res.json()) .then( data => dispatch({ type: 'LOAD_OTHER_DATA_SUCCESS', data }), err => dispatch({ type: 'LOAD_OTHER_DATA_FAILURE', err }) ); } function loadAllData(userId) { return dispatch => Promise.all( dispatch(loadSomeData(userId)), // just dispatch normally! dispatch(loadOtherData(userId)) // just dispatch normally! ); } // component componentWillMount() { this.props.dispatch(loadAllData(this.props.userId)); // just dispatch normally! } function loadSomeData(userId) { // Thanks to Redux Thunk I can use getState() here without changing callers return (dispatch, getState) => { if (getState().data[userId].isLoaded) { return Promise.resolve(); } fetch(`http://data.com/${userId}`) .then(res => res.json()) .then( data => dispatch({ type: 'LOAD_SOME_DATA_SUCCESS', data }), err => dispatch({ type: 'LOAD_SOME_DATA_FAILURE', err }) ); } } |
If you need to change it to be synchronous, you can also do this without changing any calling code:
1 2 3 4 5 6 7 |
// I can change it to be a regular action creator without touching callers function loadSomeData(userId) { return { type: 'LOAD_SOME_DATA_SUCCESS', data: localStorage.getItem('my-data') } } |
ref – https://www.freecodecamp.org/news/javascript-debounce-example/
Upon first click, debounce function returns a function.
1 2 3 4 |
return (...args) => { // 1st function on the stack clearTimeout(timer); timer = setTimeout(() => {}, timeout); }; |
This function goes onto the local stack.
It has a local timer variable that points to a setTimeout.
Initially, its suppose to execute our function parameter after 300 ms.
We simulate a button click by executing this function reference.
But if within the 300 ms the user clicks again (a second click!).
Thus, a SECOND debounce function call will return a 2nd function that gets pushed onto the local stack. The 2nd function will then execute and using clearTimeout will clear the previously set timer from the FIRST CLICK.
1 2 3 4 |
return (...args) => { // 2nd function on the stack clearTimeout(timer); // HERE! Cancels the setTimeout in the 1st function timer = setTimeout(() => {}, timeout); }; |
Then, the first setTimeout finishes executing (because it was just cleared by our 2nd click) and gets popped from the stack.
So now, only our 2nd click’s inner function is executing on the local stack. Its timer is processing the 300 ms via setTimeout. If we do not click again, then the 300 ms will finish, and thus our saveInput will execute..successfully finishing this debounced operation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
function debounce(func, timeout = 300){ console.log('2) -- debounce called --'); let timer; console.log('3) return you a function reference'); return (...args) => { console.log('5) inner function start: CLEAR PREVIOUSLY set timer') clearTimeout(timer); console.log('set new timer for 300 ms'); timer = setTimeout( () => { console.log(`6) ${timeout} ms over, lets execute the func`); func.apply(this, args); }, timeout); }; } function saveInput(param){ console.log('Saving data...', param); } console.log('1) start, put func inside debounce parameter') const processChange = debounce((param) => saveInput(param)); console.log('4) Executing function reference!'); processChange('test'); |
1) start, put func inside debounce parameter
2) — debounce called —
3) return you a function reference
4) Executing function reference!
5) inner function start: CLEAR PREVIOUSLY set timer
set new timer for 300 ms
6) 300 ms over, lets execute the func
Saving data… test
The issue here is that on our client, say we update a word like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const updateWord = async (token, updatedWordData) => { console.log('-- updateWord --'); const config = { headers: { Authorization: `Bearer ${token}`, }, } console.log('PUT ', API_URL + updatedWordData.date); console.log(updatedWordData); const response = await axios.put(API_URL, updatedWordData, config); // at this point, response is a Promise. return response.data; // we must return the data! DO NOT return Promise } |
Because when dispatch our updateWord function, if we were to return a Promise object…
1 |
dispatch(updateWord(formData)); |
…it will be assigned to state’s wordData, which is a violation because state can only take serializable value.
src/features/words/wordSlice.js
1 2 3 4 5 |
.addCase(updateWord.fulfilled, (state, action) => { ... state.wordData = action.payload ... }) |
A Promise is not serializable, so thus it will give an error like so: