console.log
- Manage clipboard in JavaScript
Set
operation- How to use pnpm
- Abort a browser event with
signal
- 使用
in
操作符来检查对象是否具有特定属性 isEmpty
in Lodash- Default value and non-null operator
- Explanation for
align
in Flex layout - Events in React NativeGesture Handler
- Organizing multiple Git identities
- Promise in dive
- Github Emoji supported
- Improve Git perf in monorepo
- JSDoc
- Use local NodeJS version by
fnm
- make your console.log stand out by adding some CSS to it
- An Interactive Guide for Flex and Grid
- Conditional Object Properties Using Spread in JavaScript
- How to Fix 'Cannot Be Opened Because the Developer Cannot be Verified' Error on Mac
- Build an API with Node.js, Express, and TypeScript
- Node version management
- What's new in ECMAScript 2023
- Customize Theme in Ant Design
- Setting JAVA_HOME and PATH Variables
- Work with Yup type errors
- Handle Rejection of Promise
- Radius tip in design
- VS Code Shortcut
- TypeScript for API contract
- zsh alias
- Destruct dynamic property from an object
- Memorize the setter in hooks
- Possible reason on Prettier and ESLint conflict
- Protected Route with React Route
- The difference between
for..of
andfor..in
- Good advice on JSX conditionals
- Format a list with
Intl
- Unicode Segmentation in JavaScrip
- Input emoji
- Show hidden files in MacOS
- How to Trim Strings in JavaScript
- Convert arrays to human-readable lists
- Footnotes now supported in Markdown fields
RouteProps
&RouteComponentProps
- A Complete Guide to Testing React Hooks
- Download with
<a></a>
- A
setTimeout
demo in create-snowpack-app - A snippet of
useContext
- React Children And Iteration Methods
- New shortcut for any GitHub repo
- Protected Routes in React
- Git track remote branches
- Git
switch
andrestore
- CSS 繁简转换
<input />
valueuseMemo
is not restricted to use on the most top of code- Debug setting for CRA in VS Code
<input />
events tips- Accessing
NODE_ENV
- VS Code IntelliSense
<fieldset />
- Serving sharp images to high density screens
- How to Dynamically Import ECMAScript Modules
- ES2021
- Optional chain tip
- The extended types can be narrower than its parent ones only
- 3 Useful TypeScript Patterns to Keep in Your Back Pocket
- React uncontrolled input issue
- Enums
- TypeScript Config for
import
statement shortcut - Google layout demonstration
- HTML Tips
- Journey of Improving React App Performance
- navigate directories faster with bash
- React 17 adds support for KeyboardEvent.code property to SyntheticEvent
- Yarn WorkSpace
- Emotion Official Docs
before
andafter
in EmotionJS- Object Styles
keyframes
in Emotion with AntDesign overwrite- Pass
props
to the children - styled-components theme with TypeScript support
- Styled Components Best Practices
- How to use CSS3 variables in styled-components
- Handling Static Assets in Jest
- ES2021 Logical assignment operators
- MacOS
- React Native 0.64
- React Types
addEventListener
support abort- Installing react-native-unimodules
- Using Libraries in React Native
$$
in DevTools- Building React Components Using Children Props and Context API
- Re-export a component as default
- Generic type support default type
Intl
API- Static property for both class component & function component
- Use derived types
- Search and replace in VSCode
- Tips from React Native document
useRef
andsetTimeout
in TypeScript- Record screen in iOS Simulator
onBlur
andonFocus
issues ofTextInput
- Query keys from React Query
React.memo
hint- Destructuring Object conditionally
- Define
Enums
in JSDoc - Get key types from
Enum
- Default parameter in ES6
- Styled-Components features not support in React Native
- Styled Props tips in React Native
- Use React Native build-in style types
- Styled-components for React Native
- customize component display name
- Default
backgroundColor
for<View />
is#FAFAFA
- A code style
- How to use fonts in ReactNative
- non-null assertion operator (the postfix ! character).
- Fix issues on iOS simulator
- How to access dot file or folder in MacOS
- Fish shell
- Cold boot virtual android device in Android Studio
- Use 'open' command to open a file with its default browser.
- How to dark mode
package.json
version- Create React App with TypeScript issue
- How to rename Git branch
- Add environment variables to CodeSandbox
- ContextAPI Initiative in twilio-video-app-react
- React native tool port
- Array.prototype.slice
- Debug practices
- How to Update Angular
- 可观察对象的退订
- Disabled type check temporarily
- DOM 事件的行内回调形式
- Prettier End of Line
- Safe area to display in Expo
- Git rebase
- Remove a file from Git commit
- Make
yarn golbal add
works clamp()
function in CSS- How Array sort works in JavaScript
- How to await for multiple results in parallel
- Set
const
variable asreadonly
- Catch error in
async / await
- Make sure property exist
- Initiate
tsconfig.json
- Unit test resources
- Testing React components that update asynchronously with React Testing Library
- Avoid unnecessary function invoking
- Multifolder in VS Code
npm list -g
- Styled-Components issue in React Native
- Highlight Git diff in Markdown
https://www.youtube.com/watch?v=Ozg5UqaD5fg
//
console.log(obj);
console.dir(obj, {
depth: Infinity,
})
https://alexharri.com/blog/clipboard
document.addEventListener("copy", (e) => {
e.preventDefault();
e.clipboardData.setData("text/plain", "Hello");
});
document.dispatchEvent(new ClipboardEvent("copy", {
clipboardData: new DataTransfer(),
}));
https://www.sonarsource.com/blog/union-intersection-difference-javascript-sets/
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const allLanguages = frontEndLanguages.union(backEndLanguages);
// => Set {"JavaScript", "HTML", "CSS", "Python", "Java"}
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const frontAndBackEnd = frontEndLanguages.intersection(backEndLanguages);
// => Set {"JavaScript"}
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = frontEndLanguages.difference(backEndLanguages);
// => Set {"HTML", "CSS"}
const onlyBackEnd = backEndLanguages.difference(frontEndLanguages);
// => Set {"Python", "Java"}
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const interpretedLanguages = new Set(["JavaScript", "Ruby", "Python"]);
const compiledLanguages = new Set(["Java", "C++", "TypeScript"]);
interpretedLanguages.isDisjointFrom(compiledLanguages);
// => true
frontEndLanguages.isDisjointFrom(interpretedLanguages);
// => false
https://youtu.be/LyNEHVzJ24U?t=221
https://youtu.be/LyNEHVzJ24U?t=570
https://www.freecodecamp.org/news/how-to-use-pnpm/
Set up an alias
# .bashrc, .zshrc, or config.fish
alias pn=pnpm
Setup Tab-Completion
Method 1:
pnpm install-completion
Usage
pnpm init
pnpm add PACKAGE
pnpm add -D PACKAGE
pnpm add -g PACKAGE
# Same
pnpm install
pnpm i
# Same
pnpm run build
pnpm build
# Same
pnpm exec tsc --init
pnpm tsc --init
pnpm up
pnpm up --latest
pnpm rm express
pnpm rm -g nodemon
How pnpm <command> Works
pnpm <command> works like this:
- If <command> is a pnpm command (that is add, install and so on), execute that command.
- Else if <command> is a script found in package.json, execute pnpm run <command>.
- Else execute pnpm exec <command>.
npx
Alternative
pnpm dlx
const {signal} = new AbortController();
document.addEventListener("click", () => {}, { signal });
https://www.apollographql.com/tutorials/intro-typescript/13-using-models
// not work
{
tracks: ({ tracks, id }, _, { dataSources }) => {
return tracks.items
? tracks.items.map(({ track }) => track)
: dataSources.spotifyAPI.getTracks(id);
},
}
// work
{
tracks: ({ tracks, id }, _, { dataSources }) => {
if ("items" in 🚀 tracks) {
return tracks.items.map(({ track }) => track);
}
return dataSources.spotifyAPI.getTracks(id);
},
}
console.log("[0]", _.isEmpty(0)); // true as no `length` props
console.log("[1]", _.isEmpty(1)); // true as no `length` props
console.log("[String]", _.isEmpty("Foo")); // false
console.log("[Empty string]", _.isEmpty("")); // true
console.log("True", _.isEmpty(true)); // true
console.log("False", _.isEmpty(false)); // true
console.log("Undefined", _.isEmpty(undefined)); // true
console.log("Null", _.isEmpty(null)); // true
console.log("[]", _.isEmpty([])); // true
console.log(
"Fn",
_.isEmpty(() => undefined)
); // true
// both produces 9
const foo = null ?? 9;
const foo = undefined ?? 9;
function foo(props = 9) {
return foo;
}
// return null
foo(null);
// return 9
foo(undefined);
const o = { a: null, b: undefined, c: 9 };
const { a = 0, b = 0, c = 0 } = o;
a; // null
b; // 0
c; // 9
https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/states-events/#state-flows
https://garrit.xyz/posts/2023-10-13-organizing-multiple-git-identities
in ~/.gitconfig file:
[user]
name = Garrit Franke
email = garrit@slashdev.space
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
[includeIf "gitdir:~/work/client2/"]
path = ~/.gitconfig-client2
[includeIf "gitdir:~/sources/"]
path = ~/.gitconfig-personal
https://www.lydiahallie.com/blog/promise-execution
The Microtask Queue is a specialized queue in the event loop. When the Call Stack is empty, the event loop first processes tasks waiting in the Microtask Queue before handling tasks from the regular Task Queue (also called "callback queue" or "macrotask queue").
✅, ✅ and ❌.
// .git/config, required git version >= 2.37.0
[core]
fsmonitor = true
untrackedcache = true
https://alexharri.com/blog/jsdoc-as-an-alternative-typescript-syntax
Enable checkJs
checkJs
needs to be enabled in your tsconfig.json
for type errors to be emitted. If you don't enable checkJs
, your JSDoc comments will only be used for IDE annotations—not type checking.
https://github.com/Schniz/fnm#fish-shell
Create .node-version
file in project root with NodeJS version.
.npmrc
won't work.
16
For fish
shell, create `~/.config/fish/conf.d/fnm.fish`` add this line to it:
fnm env --use-on-cd | source
Restart your shell for the changes to take effect.
https://twitter.com/i/status/1749439583018709448
Flex in CSS
https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/
Grid in CSS
https://www.joshwcomeau.com/css/interactive-guide-to-grid/
阮一峰的解释
https://es6.ruanyifeng.com/#docs/object#%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6
如果扩展运算符后面不是对象,则会自动将其转为对象。
// 等同于 {...Object(1)}
{...1} // {}
上面代码中,扩展运算符后面是整数1
,会自动转为数值的包装对象Number{1}
。由于该对象没有自身属性,所以返回一个空对象。
下面的例子都是类似的道理。
// 等同于 {...Object(1)}
{...1} // {}
// 等同于 {...Object(true)}
{...true} // {}
// 等同于 {...Object(undefined)}
{...undefined} // {}
// 等同于 {...Object(null)}
{...null} // {}
与数组的扩展运算符一样,对象的扩展运算符后面可以跟表达式。
const obj = {
...(x > 1 ? { a: 1 } : {}),
b: 2,
};
It also works on Array.
扩展运算符后面还可以放置表达式。
const arr = [...(x > 0 ? ["a"] : []), "b"];
如果扩展运算符后面是一个空数组,则不产生任何效果。
[...[], 1];
// [1]
注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
(...[1, 2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1, 2])
// 1 2
https://www.lifewire.com/fix-developer-cannot-be-verified-error-5183898
https://www.split.io/blog/node-js-typescript-express-tutorial/
npm i -D nodemon ts-node
"scripts": {
"serve": "nodemon app.ts"
}
Remember, the
ts-node
package makes this possible under the hood.
https://developers.cloudflare.com/d1/get-started/#prerequisites
Use a Node version manager like Volta or nvm to avoid permission issues and change Node.js versions.
Array find from last
const isEven = (number) => number % 2 === 0;
const numbers = [1, 2, 3, 4];
// from first to the last lookup
console.log(numbers.find(isEven));
// 2
console.log(numbers.findIndex(isEven));
// 1
// from last to the first lookup
console.log(numbers.findLast(isEven));
// 4
console.log(numbers.findLastIndex(isEven));
// 3
Symbols as WeakMap keys
const weak = new WeakMap();
const key = Symbol("ref");
weak.set(key, "ECMAScript 2023");
console.log(weak.get(key));
// ECMAScript 2023
Change Array by Copy
const original = [1, 2, 3, 4];
const reversed = original.toReversed();
console.log(original);
// [ 1, 2, 3, 4 ]
console.log(reversed);
// [ 4, 3, 2, 1 ]
const original = [1, 3, 2, 4];
const sorted = original.toSorted();
console.log(original);
// [ 1, 3, 2, 4 ]
console.log(sorted);
// [ 1, 2, 3, 4 ]
const original = [1, 4];
const spliced = original.toSpliced(1, 0, 2, 3);
console.log(original);
// [ 1, 4 ]
console.log(spliced);
// [ 1, 2, 3, 4 ]
const original = [1, 2, 2, 4];
const withThree = original.with(2, 3);
console.log(original);
// [ 1, 2, 2, 4 ]
console.log(withThree);
// [ 1, 2, 3, 4 ]
https://ant.design/docs/react/customize-theme
Customize theme with ConfigProvider
import { Button, ConfigProvider } from "antd";
import React from "react";
const App: React.FC = () => (
<ConfigProvider
theme={{
token: {
colorPrimary: "#00b96b",
},
}}
>
<Button />
</ConfigProvider>
);
export default App;
Customize Component Token
import { Checkbox, ConfigProvider, Radio } from "antd";
import React from "react";
const App: React.FC = () => (
<ConfigProvider
theme={{
components: {
Radio: {
colorPrimary: "#00b96b",
},
},
}}
>
<Radio>Radio</Radio>
<Checkbox>Checkbox</Checkbox>
</ConfigProvider>
);
export default App;
https://docs.daml.com/getting-started/path-variables.html#mac-os
echo $SHELL
For bash
echo 'export JAVA_HOME="$(/usr/libexec/java_home)"' >> ~/.bash_profile
echo 'export PATH="$HOME/.daml/bin:$PATH"' >> ~/.bash_profile
For zsh
echo 'export JAVA_HOME="$(/usr/libexec/java_home)"' >> ~/.zprofile
echo 'export PATH="$HOME/.daml/bin:$PATH"' >> ~/.zprofile
Verify Java configuration
echo $JAVA_HOME
https://stackoverflow.com/a/63795693
https://github.com/jquense/yup#schematypeerrormessage-string-schema
For a custom message to number type you should call the typeError() function:
numberField: Yup.number().typeError(numberErrorMessage).required(requiredErrorMessage),
Live: https://gist.run/?id=05c1665355c4df054096634a5be5f72c&mc_cid=51f8a86c89&mc_eid=afaf576ce2
// Rejected 1
// Rejected 3
console.log = (msg) => document.write(`${msg}<br/>`);
// then().catch()
Promise.resolve()
.then(() => {
return Promise.reject();
})
.catch(() => {
console.log("Rejected 1"); // Rejected
});
// then(..., ...)
Promise.resolve().then(
() => {
return Promise.reject(); // Unhandled rejection
},
() => {
console.log("Rejected 2");
}
);
// then().then(undefined, ...)
Promise.resolve()
.then(() => {
return Promise.reject();
})
.then(undefined, () => {
console.log("Rejected 3"); // Rejected
});
nested border radii look really funky if they're the same. To maintain the same curvature, the outer radius = inner radius + padding.
cursorWordPartLeft(Right)
:ctrl alt <-
when textInputFocusView: Move Editor into Next Group
:ctrl+k right arrow
View: Move Editor into Next Group
:ctrl+cmd+right
https://www.jonmellman.com/posts/typescript-for-api-contracts
Type Parameters: Axios
Now we can start using these type parameters in a simple example: an API like GET /api/users/:userId.
The API client might look like:
// client/api/users.ts
import axios from "axios";
type User = {
userId: number;
name: string;
};
export const getUser = async (userId: number): Promise<User> => {
const { data } = await axios.get<User>(`/api/users/${userId}`);
// 🚫 Property 'userName' does not exist on type 'User'.
console.log(data.userName);
// ✅ `data` matches the return type of the method signature.
return data;
};
Type Parameters: Express
On the express side, it’s a little more complicated. But not much.
Consider:
// server/routes/users.ts
type User = {
userId: number;
name: string;
};
export const usersRouter = express.Router();
usersRouter.get</* path params: */ { userId: number }, /* response: */ User>(
"/api/users/:userId",
async (req, res) => {
// 🚫 Property 'id' does not exist on type '{ userId: number; }'
const { id } = req.params;
// ✅ `userId` is of type `number`
const { userId } = req.params;
const user = await dao.getUser(userId);
// 🚫 Type 'undefined' is not assignable to type 'string'
user.name = undefined;
// ✅ Our response type matches the router's type signature.
res.status(200).json(user);
}
);
alias ..="cd .."
alias ...="cd ../.."
const deleteUser = (id) =>
new Promise((resolve, reject) => {
// 👇👇👇👇
const { [id]: user, ...rest } = users;
if (!user) {
return setTimeout(() => reject(new Error("User not found")), 250);
}
users = { ...rest };
return setTimeout(() => resolve(true), 250);
});
https://usehooks.com/useToggle/
const useToggle = (initialState = false) => {
// Initialize the state
const [state, setState] = useState(initialState);
// Define and memorize toggler function in case we pass down the component,
const toggle = useCallback(() => setState((state) => !state), []);
return [state, toggle];
};
Place the Prettier
at the very end of extends
statement in .eslintrc
configuration file;
prettier/eslint-config-prettier#198 (comment)
{
"extends": ["plugin:prettier/recommended"],
"parserOptions": { "ecmaVersion": 2018 },
"overrides": [
{
"files": ["*.ts"],
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": ["./tsconfig.json"] },
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
+ "prettier"
]
}
]
}
rules
statement will receive the very first priority, and override the extends
configurations.
The following trailingComma
rule in .prettierrc
will be overridden by rules
in .eslintrc
.
// .eslintrc
{
"rules": {
"comma-dangle": {
"comma-dangle": ["error", "always-multiline"],
}
}
}
// .prettierrc
{
"trailingComma": "es5"
}
https://www.robinwieruch.de/react-router-private-routes/
import {
Routes,
Route,
Link,
Navigate,
Outlet,
} from 'react-router-dom';
const ProtectedRoute = ({ user, redirectPath = '/landing' }) => {
if (!user) {
return <Navigate to={redirectPath} replace />;
}
return <Outlet />;
};
const App = () => {
...
return (
<>
...
<Routes>
<Route index element={<Landing />} />
<Route path="landing" element={<Landing />} />
<Route element={<ProtectedRoute user={user} />}>
<Route path="home" element={<Home />} />
<Route path="dashboard" element={<Dashboard />} />
</Route>
<Route path="analytics" element={<Analytics />} />
<Route path="admin" element={<Admin />} />
<Route path="*" element={<p>Theres nothing here: 404!</p>} />
</Routes>
</>
);
};
The for..in
loop iterates over the object's enumerable properties.
for..of
, on the other hand, iterates over an iterable. In Javascript, an iterable is any object that defines a [Symbol.iterator]
function that returns a conforming iterator. This lets the object define how to return its elements.
const iterable = {
[Symbol.iterator]: function* () {
yield 5;
yield 6;
yield 7;
},
};
for (let i of iterable) {
console.log(i); // 5, 6, 7
}
https://thoughtspile.github.io/2022/01/17/jsx-conditionals/
{number && <JSX />}
renders 0 instead of nothing. Use{number > 0 && <JSX />}
instead.- Don’t forget the parentheses around or-conditions:
{(cond1 || cond2) && <JSX />}
- Ternaries don’t scale beyond 2 branches — try an && block per branch, or extract a function and use
if / else
. - You can’t tell if
props.children
(or any interpolated element) actually contains some content — CSS:empty
is your best bet. {condition ? <Tag props1 /> : <Tag props2 />}
will not remount Tag — use uniquekey
or separate&&
branches if you want the remount.
https://twitter.com/ericclemmons/status/1488558951008509963
https://h3manth.com/posts/unicode-segmentation-in-javascript/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter
let segmenter = new Intl.Segmenter("kn-IN", { granularity: "word" });
let input = "ಆವು ಈವಿನ ನಾವು ನೀವಿಗೆ ಆನು ತಾನದ ತನನನಾ";
let segments = segmenter.segment(input);
- Windows: win + .
- MacOS: cmd + ctrl + space
Show hidden files in MacOS
cmd + shift + .
https://dmitripavlutin.com/javascript-string-trim/
The whitespaces and line terminators
the whitespace is any character from the following list:
- SPACE (U+0020 code point)
- CHARACTER TABULATION (U+0009 code point)
- LINE TABULATION (U+000BU code point)
- FORM FEED (FF) (U+000C code point)
- NO-BREAK SPACE (U+00A0 code point)
- ZERO WIDTH NO-BREAK SPACE (U+FEFFU code point)
- Any other character from Space Separator category
the whitespace is any character from the following list:
- SPACE (U+0020 code point)
- CHARACTER TABULATION (U+0009 code point)
- LINE TABULATION (U+000BU code point)
- FORM FEED (FF) (U+000C code point)
- NO-BREAK SPACE (U+00A0 code point)
- ZERO WIDTH NO-BREAK SPACE (U+FEFFU code point)
- Any other character from Space Separator category
https://www.amitmerchant.com/how-to-convert-arrays-to-human-readable-lists-in-javascript/
const books = [
"Harry Potter",
"Bhagavad Gita",
"The Alchemist",
"Birthday Girl",
];
const listFormatter = new Intl.ListFormat("en-GB", {
style: "short",
type: "disjunction",
});
console.log(listFormatter.format(books));
// Harry Potter, Bhagavad Gita, The Alchemist, or Birthday Girl
https://github.blog/changelog/2021-09-30-footnotes-now-supported-in-markdown-fields/
Here is a simple footnote[^1]. With some additional text after it.
[^1]: My reference.
https://www.pluralsight.com/guides/react-router-typescript
We see that a Route is a React.Component that receives props in the shape of RouteProps. It then renders the component provided to it by component prop and defines what props it will pass to that component: RouteComponentProps.
https://www.toptal.com/react/testing-react-hooks-tutorial
https://shkspr.mobi/blog/2021/08/to-download-this-page-click-here/
<a href="" download="this.html">Download this page</a>
<a href="/pictures/kitten.jpg" download>Download a kitten photo!!!</a>
<a href="/6f12ec75-c4ff-401e-a542" download="puppy.png"
>Download a random puppy!</a
>
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => setCount(count + 1), 1000);
return () => clearTimeout(timer);
}, [count, setCount]);
export const useAuth = () => useContext(AuthContext);
https://www.smashingmagazine.com/2021/08/react-children-iteration-methods/
- The
React.Children
utility methods. We saw two of them:React.Children.map
to see how to use it to make compound components, andReact.Children.toArray
in depth. - We saw how
React.Children.toArray
converts opaque children prop — which could be either object, array or function — into a flat array, so that one could operate over it in required manner — sort, filter, splice, etc… - We learned that
React.Children.toArray
doesn’t traverse through React Fragments. - We learned about an open-source package called
react-keyed-flatten-children
and understood how it solves the problem. - We saw that
Children
utilities are in maintenance mode because they do not compose well.
Press . on any GitHub repo to open it in a browser builtin VS Code.
YouTube: https://www.youtube.com/watch?v=Y0-qdp-XBJg
CodeSandBox: https://codesandbox.io/s/react-protected-routes-forked-vrpc7?file=/src/protected.route.js
function App() {
return (
<div className="App">
<Switch>
<Route exact path="/" component={LandingPage} />
<ProtectedRoute path="/app" component={AppLayout} />
<Route path="*" component={() => "404 NOT FOUND"} />
</Switch>
</div>
);
}
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) => {
if (auth.isAuthenticated()) {
return <Component {...props} />;
} else {
return (
<Redirect
to={{
pathname: "/",
state: {
// access location from "props", instead of "useHistory"
from: props.location,
},
}}
/>
);
}
}}
/>
);
};
# track remote branch with remote branch name
git checkout remote-feature-branch-name
https://www.banterly.net/2021/07/31/new-in-git-switch-and-restore/
Checkout can also:
git checkout develop // brach
git checkout f8c540805b7e... // commit hash
git checkout -- test.txt // file name to restore it with the latest commit
Suppose HEAD
is develop
, the following command will restore the file with the latest commit in main
brach:
git checkout main -- test.text
It's due to the doc in git
switch
to a new brach
git switch develop
and commit
git switch -d 👈 f8c540805b7e16753c65619ca3d7514178353f39
or create a brach and switch to it
git switch -c 👈 new_branch
restore
a file
git restore -- test.txt
CSS 命令 ,可以让网站字体从简体变成繁体。
font-variant-east-asian: traditional;
By default, the type of input's value is string
.
Accessing valueAsDate
and valueAsNumber
, it will return intended type directly.
<input
onChange={(event) => console.log(event.target.valueAsDate)}
onChange={(event) => console.log(event.target.valueAsNumber)}
/>
export const useTodosQuery = () => {
const queryInfo = useQuery(['todos'], fetchTodos)
return {
...queryInfo,
data: React.useMemo( 👈
() => queryInfo.data?.map((todo) => todo.name.toUpperCase()),
[queryInfo.data]
),
}
}
const transformTodoNames = (data: Todos) =>
data.map((todo) => todo.name.toUpperCase())
export const useTodosQuery = () =>
useQuery(['todos'], fetchTodos, {
// ✅ memoizes with useCallback
select: React.useCallback( 👈
(data: Todos) => data.map((todo) => todo.name.toUpperCase()),
[]
),
})
{
"type": "pwa-chrome",
"request": "launch",
"name": "Chrome",
"skipFiles": ["node_modules"],
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src"
}
When listening to value changes mind that:
❌ Cross mark change
event doesn't emit unless user e.g. leaves the input
❌ Cross mark keypress
/keydown
doesn't emit eg. when the input is changed with a mouse click
✅ White heavy check mark but input
event does emit every time user updates the field
https://twitter.com/stackblitz/status/1416033954034036745
https://stackoverflow.com/questions/45194598/using-process-env-in-typescript
There's no guarantee of what (if any) environment variables are going to be available in a Node process - the NODE_ENV variable is just a convention that was popularised by Express, rather than something built in to Node itself. As such, it wouldn't really make sense for it to be included in the type definitions. Instead, they define process.env like this:
export interface ProcessEnv {
[key: string]: string | undefined;
}
Which means that process.env can be indexed with a string in order to get a string back (or undefined, if the variable isn't set). To fix your error, you'll have to use the index syntax:
let env = process.env["NODE_ENV"]; // 👈 That's it.
Moving "Auto Import" statement to the very top of IntelliSense menu.
Grouping and manipulating the relevant inputs together.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset
https://jakearchibald.com/2021/serving-sharp-images-to-high-density-screens/
Key points: AVIF & WebP
<picture>
<source
type="image/avif"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="2x-800.avif 800w, 2x-1200.avif 1200w, 2x-1598.avif 1598w"
sizes="
(min-width: 1066px) 743px,
(min-width: 800px) calc(75vw - 57px),
100vw
"
/>
<source
type="image/webp"
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="2x-800.webp 800w, 2x-1200.webp 1200w, 2x-1598.webp 1598w"
sizes="
(min-width: 1066px) 743px,
(min-width: 800px) calc(75vw - 57px),
100vw
"
/>
<source
media="(-webkit-min-device-pixel-ratio: 1.5)"
srcset="2x-800.jpg 800w, 2x-1200.jpg 1200w, 2x-1598.jpg 1598w"
sizes="
(min-width: 1066px) 743px,
(min-width: 800px) calc(75vw - 57px),
100vw
"
/>
<source type="image/avif" srcset="1x-743.avif" />
<source type="image/webp" srcset="1x-743.webp" />
<img src="1x-743.jpg" width="743" height="477" alt="A red panda" />
</picture>
https://dmitripavlutin.com/ecmascript-modules-dynamic-import/
// namedConcat.js
export const concat = (paramA, paramB) => paramA + paramB;
async function loadMyModule() {
const { concat } = await import("./namedConcat.js");
concat("b", "c"); // => 'bc'
}
// defaultConcat.js
export default (paramA, paramB) => paramA + paramB;
async function loadMyModule() {
const { default: defaultImport } = await import("./defaultConcat.js");
defaultImport("b", "c"); // => 'bc'
}
Logical Assignment Operators
//"Or Or Equals"
x ||= y;
x || (x = y);
// "And And Equals"
x &&= y;
x && (x = y);
// "QQ Equals"
x ??= y;
x ?? (x = y);
const updateID = (user) => {
// We can do this
if (!user.id) user.id = 1;
// Or this
user.id = user.id || 1;
// Or use logical assignment operator.
user.id ||= 1;
};
function setOpts(opts) {
opts.cat ??= "meow";
opts.dog ??= "bow";
}
setOpts({ cat: "meow" });
Numeric Separators
1_000_000_000; // Ah, so a billion
101_475_938.38; // And this is hundreds of millions
let fee = 123_00; // $123 (12300 cents, apparently)
let fee = 12_300; // $12,300 (woah, that fee!)
let amount = 12345_00; // 12,345 (1234500 cents, apparently)
let amount = 123_4500; // 123.45 (4-fixed financial)
let amount = 1_234_500; // 1,234,500
0.000_001; // 1 millionth
1e10_000; // 10^10000 -- granted, far less useful / in-range...
0xa0_b0_c0;
Promise.any and AggregateError
Promise.any([
fetch("https://v8.dev/").then(() => "home"),
fetch("https://v8.dev/blog").then(() => "blog"),
fetch("https://v8.dev/docs").then(() => "docs"),
])
.then((first) => {
// Any of the promises was fulfilled.
console.log(first);
// → 'home'
})
.catch((error) => {
// All of the promises were rejected.
console.log(error);
});
String.prototype.replaceAll
// String.prototype.replaceAll(searchValue, replaceValue)
"x".replace("", "_");
// → '_x'
"xxx".replace(/(?:)/g, "_");
// → '_x_x_x_'
"xxx".replaceAll("", "_");
// → '_x_x_x_'
WeakRefs and FinalizationRegistry Objects
let target = {};
let wr = new WeakRef(target);
//wr and target aren't the same
// Creating a new registry
const registry = new FinalizationRegistry((heldValue) => {
// ....
});
registry.register(myObject, "some value", myObject);
// ...some time later, if you don't care about `myObject` anymore...
registry.unregister(myObject);
https://swizec.com/blog/a-surprising-feature-of-javascript-optional-chaining/
object?.deepProp?.[console.log("runs if deepProp defined")];
interface Base {
common: string; // 👉 string | boolean will makes the Ex works
}
/**
* Interface 'Ex' incorrectly extends interface 'Base'.
* Types of property 'common' are incompatible.
* Type 'boolean' is not assignable to type 'string'.(2430)
*/
interface Ex extends Base {
common: boolean;
}
https://spin.atomicobject.com/2021/05/11/3-useful-typescript-patterns/
type Names = "Bob" | "Bill" | "Ben";
type JobTitles = "Welder" | "Carpenter" | "Plumber";
const JobAssignments: { [Key in Names]: JobTitles } = {
Bob: "Welder",
Bill: "Carpenter",
Ben: "Plumber",
};
function inputDoubler(input: string): string;
function inputDoubler(input: number): number;
function inputDoubler(input: string | number) {
if (typeof input === "string") {
return `${input}${input}`;
} else {
return input * 2;
}
}
function isPizza(food: Pizza | Burrito): food is Pizza {
return (<Pizza>food).ingredients.topping !== undefined;
// Or
return (food as Pizza).ingredients.topping !== undefined;
}
https://reactjs.org/docs/forms.html#controlled-input-null-value
For the error message following:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
Specifying the value prop on a controlled component prevents the user from changing the input unless you desire so. If you’ve specified a value but the input is still editable, you may have accidentally set value to undefined or null.
The following code demonstrates this. (The input is locked at first but becomes editable after a short delay.)
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function () {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
- A Enum is a set of named constants.
- Enum value can be numeric, string, or computed.
- Technically enums can be mixed with string and numeric members, but it's not clear to do it
https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-compile-time
Even though Enums are real objects that exist at runtime, the keyof
keyword works differently than you might expect for typical objects. Instead, use keyof typeof
to get a Type that represents all Enum keys as strings.
enum LogLevel {
ERROR,
WARN,
INFO,
DEBUG,
}
/**
* This is equivalent to:
* type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
*/
type LogLevelStrings = keyof typeof LogLevel;
function printImportant(key: LogLevelStrings, message: string) {
const num = LogLevel[key];
if (num <= LogLevel.WARN) {
console.log("Log level key is:", key);
console.log("Log level value is:", num);
console.log("Log level message is:", message);
}
}
printImportant("ERROR", "This is a message");
https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
enum Enum {
A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
TypeScript compiles this down to the following JavaScript:
"use strict";
var Enum;
(function (Enum) {
Enum[(Enum["A"] = 0)] = "A";
})(Enum || (Enum = {}));
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums
In modern TypeScript, you may not need an enum when an object with as const could suffice:
const enum EDirection {
Up,
Down,
Left,
Right,
}
const ODirection = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
EDirection.Up;
(enum member) EDirection.Up = 0
ODirection.Up;
(property) Up: 0
// Using the enum as a parameter
function walk(dir: EDirection) {}
// type Typeof = {
// readonly Up: 0;
// readonly Down: 1;
// readonly Left: 2;
// readonly Right: 3;
// };
type Typeof = typeof ODirection;
// type Keyof = "Up" | "Down" | "Left" | "Right"
type Keyof = keyof typeof ODirection;
type LeftRight = typeof ODirection["Left" | "Right"];
const leftRight: LeftRight = 2;
// type TypeofKeyof = 0 | 1 | 2 | 3
type TypeofKeyof = typeof ODirection[keyof typeof ODirection];
// It requires an extra line to pull out the keys
type Direction = typeof ODirection[keyof typeof ODirection];
function run(dir: Direction) {}
walk(EDirection.Left);
run(ODirection.Right);
The biggest idea is not only to differentiate object and enum, but also to explain how
typeof
andkeyof
works.
const people = {
name: "Peter",
age: 9,
};
type People = typeof people; // { name: string; age: number;}
type KeyofPeople = keyof People; // "name" | "age"
type TypeofPeople = (typeof people)[KeyofPeople]; // string | number
https://www.typescriptlang.org/docs/handbook/module-resolution.html#base-url
{
"compilerOptions": {
"baseUrl": "src"
}
}
The above configuration makes:
import { Actions, Summary } from "./src/components";
as
import { Actions, Summary } from "components";
Implemented side aligned grid with as less as two element.
<ul class="Bgzgmd">
<li>
<a href="" target="_blank">帮助</a>
</li>
<li>
<a href="" target="_blank">隐私权</a>
</li>
<li>
<a href="" target="_blank">条款</a>
</li>
</ul>
.Bgzgmd {
margin: 8px -16px; // 👈 negative margin
}
.Bgzgmd li {
margin: 0;
}
.Bgzgmd a {
padding: 6px 16px; // 👈 padding
}
https://markodenic.com/html-tips/
Performance tip. You can use the loading=lazy attribute to defer the loading of the image until the user scrolls to them.
<img src="image.jpg" loading="lazy" alt="Alternative Text" />
<a href="mailto:{email}?subject={**subject**}&body={content}">
Send us an email
</a>
<a href="tel:{phone}"> Call us </a>
<a href="sms:{phone}?body={content}"> Send us a message </a>
Use the start
attribute to change the starting point for your ordered lists.
<ol start="11">
<li>HTML</li>
<li>CSS</li>
</ol>
You can use the meter
element to display quantities. No JavaScript/CSS needed.
<label for="value1">Low</label>
<meter
id="value1"
min="0"
max="100"
low="30"
high="75"
optimum="80"
value="25"
></meter>
<label for="value2">Medium</label>
<meter
id="value2"
min="0"
max="100"
low="30"
high="75"
optimum="80"
value="50"
></meter>
<label for="value3">High</label>
<meter
id="value3"
min="0"
max="100"
low="30"
high="75"
optimum="80"
value="80"
></meter>
<div class="wrapper">
<h1>Native HTML Search</h1>
<input list="items" />
<datalist id="items">
<option value="Marko Denic"></option>
<option value="FreeCodeCamp"></option>
<option value="FreeCodeTools"></option>
<option value="Web Development"></option>
<option value="Web Developer"></option>
</datalist>
</div>
You can use the fieldset
element to group several controls as well as labels (label
) within a web form.
<form>
<fieldset>
<legend>Choose your favorite language</legend>
<input type="radio" id="javascript" name="language" />
<label for="javascript">JavaScript</label><br />
<input type="radio" id="python" name="language" />
<label for="python">Python</label><br />
<input type="radio" id="java" name="language" />
<label for="java">Java</label>
</fieldset>
</form>
If you want to open all links in the document in a new tab, you can use base
element:
<head>
<base target="_blank" />
</head>
<!-- This link will open in a new tab. -->
<div class="wrapper">
This link will be opened in a new tab:
<a href="https://freecodetools.org/"> Free Code Tools </a>
<p>
Read more: <br /><a
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base"
>
MDN Documentation
</a>
</p>
</div>
To refresh your website’s favicon you can force browsers to download a new version by adding ?v=2
to the filename.
This is especially helpful in production to make sure the users get the new version.
<link rel="icon" href="/favicon.ico?v=2" />
Use the spellcheck attribute to define whether the element may be checked for spelling errors.
<label for="input1">spellcheck="true"</label>
<input type="text" id="input1" spellcheck="true" />
<label for="input2">spellcheck="false"</label>
<input type="text" id="input2" spellcheck="false" />
You can use <input type="range">
to create sliders.
<label for="volume">Volume: </label>
<input type="range" id="volume" name="volume" min="0" max="20" />
<label for="result">Your choice: </label>
<input type="number" id="result" name="result" />
You can use the details
element to create a native HTML accordion.
<div class="wrapper">
<details>
<summary>Click me to see more details</summary>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</details>
</div>
You can use the <mark>
tag to highlight text.
You can use the download
attribute in your links to download the file instead of navigating to it.
<a href="path/to/file" download> Download </a>
Use the .webp
image format to make images smaller and boost the performance of your website.
<picture>
<!-- load .webp image if supported -->
<source srcset="logo.webp" type="image/webp" />
<!--
Fallback if `.webp` images or <picture> tag
not supported by the browser.
-->
<img src="logo.png" alt="logo" />
</picture>
Use the type="search"
for your search inputs and you get the “clear” button for free.
https://medium.com/technogise/journey-of-improving-react-app-performance-by-10x-9195d4b483d4
class Parent extends Component {
render() {
return <Child onClick={() => console.log("You clicked!")} />;
}
}
- It will always trigger a re-render of the component even if there is no change in the props.
- It increases the memory footprint of the app. (Refer: Function spaces in Memory snapshot of Firefox)
class Parent extends Component {
render() {
return (
<Modal show={isOpen}>
<Modal.Header>
Hello
</Modal.Header>
<Modal.Body>
I am a Modal
</Modal.Body>
</Modal>
);
}
Avoided rendering these components until they are needed i.e. Conditional Rendering.
This decreased the memory footprint from 500Mb to 150Mb. 😄
Improving above example as below:
class Parent extends Component {
render() {
return (
<Modal show={isOpen}>
<Modal.Header>
Hello
</Modal.Header>
<Modal.Body>
I am a Modal
</Modal.Body>
</Modal>
);
}
const userSubscription = getUserSubscription();
const userDetails = getUserDetails();
const userNotifications = getUserNotifications();
Solution: We identified that most of the API calls can be made to execute in parallel. So, we used Promise.all()
to help us send all these API calls parallelly.
This decreased the initial page load time and other pages by 30%.
const [userSubscription, userDetails, userNotifications] = await Promise.all([
getUserSubscription(),
getUserDetails(),
getUserNotifications(),
]);
https://mhoffman.github.io/2015/05/21/how-to-navigate-directories-with-the-shell.html
~
Home directory-
Previous directory!$
is an alias for the last argument of the previous command(⚠️ DO NOT WORK in fish)
https://blog.saeloun.com/2021/04/23/react-keyboard-event-code
// Before
const handleKeyDown = (event) => {
const key = event.nativeEvent.code;
switch (key) {
case "KeyW":
//moveTop();
break;
case "KeyA":
//moveLeft();
break;
case "KeyS":
//moveDown();
break;
case "KeyD":
//moveRight();
break;
default:
//custom logic
}
};
// After
const handleKeyDown = (event) => {
// We replaced the native event with the synthetic keyboard event
const key = event.code;
switch (key) {
case "KeyW":
//moveTop();
break;
case "KeyA":
//moveLeft();
break;
case "KeyS":
//moveDown();
break;
case "KeyD":
//moveRight();
break;
default:
//custom logic
}
};
https://halftheopposite.dev/post/app-yarn-typescript-esbuild-part-1 https://halftheopposite.dev/post/app-yarn-typescript-esbuild-part-2 https://halftheopposite.dev/post/app-yarn-typescript-esbuild-part-3 https://halftheopposite.dev/post/app-yarn-typescript-esbuild-part-going-further
my-app/
├─ node_modules/
├─ packages/
│ ├─ app/
│ │ ├─ package.json
│ ├─ common/
│ │ ├─ package.json
│ ├─ server/
│ │ ├─ package.json
├─ package.json
├─ tsconfig.json
├─ yarn.lock
{
"name": "my-app",
"version": "1.0",
"license": "UNLICENSED",
"private": true, // Required for Yarn WorkSpace
"workspaces": ["packages/*"],
"devDependencies": {
"typescript": "^4.2.3"
},
"scripts": {
"app": "yarn workspace @my-app/app",
"common": "yarn workspace @my-app/common",
"server": "yarn workspace @my-app/server"
}
}
package.json
in every WorkSpace module
{
"name": "@my-app/app",
"version": "0.1.0",
"license": "UNLICENSED",
"private": true
}
yarn add -D -W typescript // -W to install the dependency in WorkSpace root
Support default props, even with different default value.
The content
property of before
and after
pseudo element must be wrapped by double quote ""
or single quote ''
.
const X = styled.div`
&::before {
content: ${() => (true ? '"oo"' : `"ff"`)};
// DOES NOT WORK
content: ${() => (true ? '`oo`' : "`ff`")};
}
`;
Use function API
https://emotion.sh/docs/styled#changing-based-on-props
import styled from "@emotion/styled";
const Button = styled.button`
color: ${({ primary = false }) => (primary ? "hotpink" : "turquoise")};
font-size: ${({ primary = true }) => (primary ? "18px" : ".19em")};
`;
const Container = styled.div(({ column = true }) => ({
// This Object can be replace with ES6 template string
// `display: flex;`
display: "flex",
flexDirection: column && "column",
}));
render(
<Container>
<Button>This is a regular button.</Button>
<Button primary={false}>This is a primary button.</Button>
</Container>
);
Use Object API
https://emotion.sh/docs/styled#changing-based-on-props
import styled from "@emotion/styled";
const H1 = styled.h1(
{
fontSize: 20,
},
// The dynamic part happens here
(props) => ({ color: props.color })
);
render(<H1 color="lightgreen">This is lightgreen.</H1>);
CSS API
import styled from "@emotion/styled";
import { css } from "@emotion/react";
const dynamicStyle = (props) =>
css`
color: ${props.color};
`;
const Container = styled.div`
${dynamicStyle};
`;
render(<Container color="lightgreen">This is lightgreen.</Container>);
render(
<div
css={{
color: "darkorchid",
"& .name": {
// 🚀🚀🚀🚀🚀🚀
color: "orange",
},
}}
>
This is darkorchid.
<div className="name">This is orange</div>
</div>
);
export const ButtonStyled = styled(Button)`
&.ant-btn {
@keyframes waveEffect {
100% {
box-shadow: 0 0 0 ${$4E4B66};
box-shadow: 0 0 0 6px ${$4E4B66};
}
}
@keyframes fadeEffect {
100% {
opacity: 0;
}
}
React report the following message as styling an React Component with props
:
React does not recognize the
isError
prop on a DOM element.
Resolution:
No. 1 Customizing prop forwarding by shouldForwardProp
and @emotion/is-prop-valid
See also:
- https://dev-yakuza.posstree.com/en/react/emotion/does-not-recognize-props/
- https://github.com/sumup-oss/circuit-ui/blob/f2be16b8b56fa7f4c11487cdb1bec15a1129755f/packages/circuit-ui/components/Body/Body.tsx#L92
No. 2 Omit the props
const Component: FC<StyledComponentProps> = ({
// destructure the props to omit them.
bgColor: _,
isError: __,
...rest
}) => {
return <NumericFormat {...rest} />;
};
import {
createGlobalStyle,
DefaultTheme,
ThemeProvider,
} from "styled-components";
const theme: DefaultTheme = {
balabala: "red",
};
const GlobalBalabala = createGlobalStyle`
h3 {
outline: 1px solid ${({ theme }) => theme.balabala};
}
`;
const Root = () => (
<ThemeProvider theme={theme}>
<GlobalBalabala />
<App />
</ThemeProvider>
);
// tsconfig.json
{
"compilerOptions": {
"types": ["./src/types/**/*"]
}
}
// src/types/defaultTheme.d.ts
import "styled-components";
declare module "styled-components" {
export interface DefaultTheme {
balabala: string;
}
}
https://www.robinwieruch.de/styled-components
import styled, { css } from "styled-components";
const red = css`
color: red;
`;
const Headline = styled.h1`
${red}
font-size: 20px;
`;
On the left side of spectrum
const Section = styled.section`
border-bottom: 1px solid grey;
padding: 20px;
`;
const Headline = styled.h1`
color: red;
`;
const Text = styled.span`
padding: 10px;
`;
const Content = ({ title, children }) => {
return (
<Section>
<Headline>{title}</Headline>
<Text>{children}</Text>
</Section>
);
};
On the other side of the spectrum,
const Container = styled.section`
border-bottom: 1px solid grey;
padding: 20px;
h1 {
color: red;
}
.text {
padding: 10px;
}
`;
const Content = ({ title, children }) => {
return (
<Container>
<h1>{title}</h1>
<span className="text">{children}</span>
</Container>
);
};
https://epicreact.dev/css-variables/
body[data-theme="light"] {
--colors-primary: deeppink;
--colors-background: white;
}
body[data-theme="dark"] {
--colors-primary: lightpink;
--colors-background: black;
}
const PrimaryText = styled.div({
padding: 20,
color: "var(--colors-primary)",
backgroundColor: "var(--colors-background)",
});
https://jestjs.io/docs/webpack#handling-static-assets
// package.json
{
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
}
}
}
And the mock files themselves:
// __mocks__/styleMock.js
module.exports = {};
// __mocks__/fileMock.js
module.exports = "test-file-stub";
See also: Mocking CSS Modules
https://exploringjs.com/impatient-js/ch_operators.html#logical-assignment-operators
Logical assignment operators work differently from other compound assignment operators:
Assignment operator | Equivalent to | Only assigns if a is |
---|---|---|
a ||= b | a || (a = b) | Falsy |
a &&= b | a && (a = b) | Truthy |
a ??= b | a ?? (a = b) | Nullish |
Why is a||= b
equivalent to the following expression?
a || (a = b);
Why not to this expression?
a = a || b;
The former expression has the benefit of short-circuiting: The assignment is only evaluated if a evaluates to false. Therefore, the assignment is only performed if it’s necessary. In contrast, the latter expression always performs an assignment.
In LaunchPad, holding Option key can uninstall apps instantly.
Before:
import { MyFunction } from "my-module";
const MyComponent = (props) => {
const result = MyFunction();
return <Text>{result}</Text>;
};
After:
const MyComponent = (props) => {
const result = require("my-module").MyFunction();
return <Text>{result}</Text>;
};
The main change is a new JSX transform enabling files to no longer need to import React
to be able to use JSX.
https://reactnative.dev/docs/flatlist#listemptycomponent
- React Component (e.g. SomeComponent)
- React element (e.g. )
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters
https://jakearchibald.com/2021/function-callback-risks/
const controller = new AbortController();
const { signal } = controller;
el.addEventListener("mousemove", callback, { signal });
el.addEventListener("pointermove", callback, { signal });
el.addEventListener("touchmove", callback, { signal });
// Later, remove all three listeners:
controller.abort();
https://docs.expo.io/bare/installing-unimodules/
This library contains infrastructure and a small set of foundational libraries and interfaces that are commonly depended on by other modules. You can install react-native-unimodules in any react-native app, and once it is installed you can use most of the libraries from the Expo SDK, like expo-camera, expo-media-library and many more.
If you are creating a new project, we recommend using
npx create-react-native-app
(https://github.com/expo/create-react-native-app) instead ofnpx react-native init
because it will handle the following configuration for you automatically.
https://reactnative.dev/docs/libraries
React Native Directory https://reactnative.directory/
React Native uses CocoaPods to manage iOS project dependencies and most React Native libraries follow this same convention.
Run pod install
in our ios directory in order to link it to our native iOS project. A shortcut for doing this without switching to the ios directory is to run:
npx pod-install
Once this is complete, re-build the app binary to start using your new library:
npx react-native run-ios
React Native uses Gradle to manage Android project dependencies. After you install a library with native dependencies, you will need to re-build the app binary to use your new library:
npx react-native run-android
In DevTools, $$
is shorthand for
Array.from(document.querySelectorAll());
Leran more https://developers.google.com/web/tools/chrome-devtools/console/utilities
What we need first is React.Children.toArray(children) to always handle children as an array because when there is only one component, it will not be an array by default.
https://reactjs.org/docs/code-splitting.html#named-exports
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default 👈 } from "./ManyComponents.js";
https://www.smashingmagazine.com/2021/01/dynamic-static-typing-typescript/
type ServerRequest<Met extends Methods, Par extends string = string> = {
method: Met;
params: Record<Par, string>;
};
As a bonus and because Intl
is part of ECMAScript
, it is also available in Node.js. You will want to be more careful with defaults when working on server though. The time zone, for example, is going to be where the server is located, not the time zone of your user. I recommend working in UTC
on servers and using users’ time zones when displaying the datetime.
class C extents Component {
static sharedElement = (route, otherRoute, showing) => {}
}
const C = () => (<></>)
C.sharedElement = (route, otherRoute, showing) => {}
const theme = {
color: {},
spacing: {},
};
export type Theme = typeof theme;
https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options
In the two input boxes below the search box, you can enter patterns to include or exclude from the search. If you enter example, that will match every folder and file named example in the workspace. If you enter ./example, that will match the folder example/ at the top level of your workspace. Use , to separate multiple patterns. Paths must use forward slashes. You can also use glob syntax:
*
to match one or more characters in a path segment?
to match on one character in a path segment**
to match any number of path segments, including none{}
to group conditions (for example {**/*.html,**/*.txt} matches all HTML and text files)[]
to declare a range of characters to match (example.[0-9] to match on example.0, example.1, …)
https://reactnative.dev/docs/image#resizemode
Determines how to resize the image when the frame doesn't match the raw image dimensions. Defaults to cover.
-
cover
: Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding). -
contain
: Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding). -
stretch
: Scale width and height independently, This may change the aspect ratio of the src. -
repeat
: Repeat the image to cover the frame of the view. The image will keep its size and aspect ratio, unless it is larger than the view, in which case it will be scaled down uniformly so that it is contained in the view. -
center
: Center the image in the view along both dimensions. If the image is larger than the view, scale it down uniformly so that it is contained in the view.
https://reactnative.dev/docs/flexbox#flex-wrap
flexDirection
defaulting tocolumn
instead ofrow
;alignContent
defaulting toflex-start
instead ofstretch
;flexShrink
defaulting to0
instead of1
;flex
parameter only supporting a single number.
https://stackoverflow.com/questions/52892304/style-vs-contentcontainerstyle-in-scrollview
ScrollView
is a special kind of View, which has two parts:
- Container (the grey box), it's the outside View, its height can't exceed 100% of the window height.
- Content (marked in blue) is the inner part, it can be higher than the window height, it's what's moving inside the container.
ScrollView style
defines the outer container of the ScrollView
, e.g its height
and relations
to siblings elements
ScrollView contentContainerStyle
defines the inner container of it, e.g items alignItems
, padding
, etc
https://reactnative.dev/docs/scrollview
Keep in mind that ScrollViews must have a bounded height in order to work, since they contain unbounded-height children into a bounded container (via a scroll interaction). In order to bound the height of a ScrollView, either set the height of the view directly (discouraged) or make sure all parent views have bounded height. Forgetting to transfer {flex: 1} down the view stack can lead to errors here, which the element inspector makes quick to debug.
<ScrollView>
vs <FlatList>
- which one to use?
ScrollView
renders all its react child components at once, but this has a performance downside.
FlatList
renders items lazily, when they are about to appear, and removes items that scroll way off screen to save memory and processing time.
FlatList
is also handy if you want to render separators between your items, multiple columns, infinite scroll loading, or any number of other features it supports out of the box.
https://reactnative.dev/docs/scrollview#contentcontainerstyle
While designing customer component, we can use contentContainerStyle
to define the component's container style.
export const C = ({ contentContainerStyle, style }) => (
<View style={[styles.container, ...contentContainerStyle 👈]}>
<Text style={[styles.content, ...styles 👈]}>Hello</Text>
</View>
)
## Set `<Text />` as block
https://reactnative.dev/docs/text
```js
const TextInANest = () => {
const titleText = useState("Bird's Nest"); // 👈 useful if need value only,
const bodyText = useState("This is not really a bird nest.");
return (
// <Text /> can be set as block 👇
<Text style={[styles.titleText, {display: 'flex'}]}>
{titleText}
</Text>
);
};
https://reactnative.dev/docs/view
Views are designed to be used with StyleSheet for clarity and performance, although inline styles are also supported.
It's also a demonstration of how to use
setTimeout
instead ofsetInterval
.
const timer = useRef<ReturnType<typeof setTimeout> 👈 >(null!);
useEffect(() => {
timer.current = setTimeout(function updater() {
setCurrent((current + 1) % length);
timer.current = setTimeout(updater, duration);
}, duration);
return () => {
clearTimeout(timer.current);
};
}, [current, duration, length]);
https://stackoverflow.com/questions/25797990/capture-ios-simulator-video-for-app-preview
In Android, the following code will print undefined
.
const onFocus = ({ nativeEvent: { text } }) => console.log(text);
const onBlur = ({ nativeEvent: { text } }) => console.log(text);
How to fix
1️⃣ access the value from some kind of store.
const [value, setValue] = useState("");
const onChangeText = (text) => setValue(text);
const onFocus = () => console.log(value);
const onEndEditing = () => console.log(value);
2️⃣ use onEndEditing
instead of onBlur
React Query use query keys to unique the cache for data.
All the variables which effect the query should be listed in the query keys.
Of array form
1️⃣ Both of the following query forms work, but the order matters in array form.
2️⃣ The first string type item is ❓ optional, the variables are enough to make the cache invalidate.
export const useTransferWiseQuote = (initialParams) => {
const { data: TransferWiseUser } = useQuery(queryKeys.twUser, getTwId);
const { twCustomerId } = TransferWiseUser;
const customerId = `tw_customer_id=${twCustomerId}`;
const [
{ sourceAmount, sourceCurrency, targetAmount, targetCurrency },
setParams,
] = useState(initialParams);
const initialData = useMemo(
() => ({
fee: 0,
formattedEstimatedDelivery: "",
id: "",
rate: 0,
rateType: "",
targetAmount: 0,
...initialParams,
}),
[initialParams]
);
const amountKey = sourceAmount ? "sourceAmount" : "targetAmount";
const amountValue = sourceAmount || targetAmount;
const amountString = `${amountKey}=${amountValue}`;
const sourceCurrencyString = `sourceCurrency=${sourceCurrency}`;
const targetCurrencyString = `targetCurrency=${targetCurrency}`;
const joined = [
customerId,
amountString,
sourceCurrencyString,
targetCurrencyString,
].join("&");
const url = `${ApiEndpointTransferWise}/quotes/v1/getQuote?${joined}`;
const { error, data } = useQuery(
[
queryKeys.twQuote, // 2️⃣ Optional
{
sourceAmount, // 1️⃣ Order does NOT matters
sourceCurrency,
targetAmount,
targetCurrency,
},
],
() => httpRequest.get(url),
{ initialData }
);
const { error, data } = useQuery(
[
queryKeys.twQuote, // 2️⃣ Optional
sourceAmount, // 1️⃣ Order matters
sourceCurrency,
targetAmount,
targetCurrency,
],
() => httpRequest.get(url),
{ initialData }
);
return [data, setParams];
};
https://www.youtube.com/watch?v=IuXpqUxJG90
By default it will only shallowly compare complex objects in the props object. So if a callback is passed as inline arrow function, it will cause a rerender.
Try to avoid use inline arrow function, or wrap it with useCallback
const conditionallyProperty = { ...(bool ? { foo: "Foo" } : {}) };
/**
* @typedef {"keyvalue" | "bar" | "timeseries" | "pie" | "table"} MetricFormat
*/
/**
* @param format {MetricFormat}
*/
export function fetchMetric(format) {
return fetch(`/`, format);
}
https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-compile-time
enum LogLevel {
ERROR,
WARN,
INFO,
DEBUG,
}
/**
* This is equivalent to:
* type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
*/
type LogLevelStrings = keyof typeof LogLevel; // 👈 Got it.
function printImportant(key: LogLevelStrings, message: string) {
const num = LogLevel[key];
if (num <= LogLevel.WARN) {
console.log("Log level key is:", key);
console.log("Log level value is:", num);
console.log("Log level message is:", message);
}
}
printImportant("ERROR", "This is a message");
如果传入 undefined,将触发该参数等于默认值,null 则没有这个效果。
- Note that the
flex
property works like CSS shorthand, and not the legacyflex
property in React Native. Settingflex: 1
setsflexShrink
to1
in addition to settingflexGrow
to1
andflexBasis
to0
. - cannot use the
keyframes
andcreateGlobalStyle
helpers since React Native doesn't supportkeyframes
orglobal
styles. - You will be warned if you use media queries or nest your CSS.
The following style props can be used as component props directly.
https://reactnative.dev/docs/view-style-props
Component will be styled with height and box shadow(Android only)
Although TypeScript complains that those two props are not available
<View
height={100} 👈
elevation={10} 👈
style={{backgroundColor: 'white'}} />
It DO NOT work on React Native for web
type ContainerProps = {
// available options: ViewStyle | TextStyle | ImageStyle
contentContainerStyle: ViewStyle; 👈
};
const Container = styled.FlatList<ContainerProps>`
flex-grow: 0;
margin: ${({contentContainerStyle: {margin}}) => margin ?? '10'}px;
`;
type ViewStyledProps = {
x?: number;
};
const ViewStyled = styled.View.attrs({x}) => ({
// The following DO NOT work.
style: {elevation: x, backgroundColor: 'red'},
}))``;
type ViewStyledProps = {
elevation?: number;
};
const AnimatedViewStyled = styled(Animated.View)
.attrs<ViewStyledProps 👈 >(props => ({}))<ViewStyledProps 👈 >`
box-shadow: ${({elevation = 10}) => `0 0 ${elevation}px rgba(255, 0, 0, 0.5)`};
background-color: white;
`;
AnimatedViewStyled.displayName = "Hello";
export const renderReactions = (
reactions,
supportedReactions,
reactionCounts,
handleReaction
) => {
const reactionsByType = {}; // 👈 a store
reactions &&
reactions.forEach((item) => {
if (reactions[item.type] === undefined) {
return (reactionsByType[item.type] = [item]);
} else {
// 👇 update store and return it.
return (reactionsByType[item.type] = [
...reactionsByType[item.type],
item,
]);
}
});
const emojiDataByType = {};
supportedReactions.forEach((e) => (emojiDataByType[e.id] = e));
const reactionTypes = supportedReactions.map((e) => e.id);
// 👇 map the store to view
return Object.keys(reactionsByType).map((type, index) =>
reactionTypes.indexOf(type) > -1 ? (
<ReactionItem
key={index}
type={type}
handleReaction={handleReaction}
reactionCounts={reactionCounts}
emojiDataByType={emojiDataByType}
/>
) : null
);
};
https://dev.to/vishalnarkhede/tutorial-how-to-build-a-slack-clone-with-react-native-part-1-37kn
-
create
react-native.config.js
in root of project.module.exports = { assets: ["./assets/fonts/"], };
-
place font files in the folder above.
-
run
npx react-native link
. -
apply font in style
const styles = StyleSheet.create({ title: { fontFamily: "Lato-Regular", }, });
https://stackoverflow.com/questions/51147704/expo-error-installing-or-running-app
1️⃣ From shell
xcrun simctl erase all
2️⃣ From Simulator menu
Simulator -> Device
-> Erase All Contents and Settings
.
- Simulator -> 'I/O' ->
Keyboard
->Connect Hardware Keyboard
, or ⌘+Shift+K
- shift + cmd + G to go to the dot file or folder.
- Use Spotlight
Set Fish as default shell in VS Code
autosuggestion
https://fishshell.com/docs/current/index.html#autosuggestions
To accept the autosuggestion (replacing the command line contents), press right arrow or Control+F. To accept the first suggested word, press Alt+→,Right or Alt+F. If the autosuggestion is not what you want, just ignore it: it won't execute unless you accept it.
- Tab completes the current token. Shift, Tab completes the current token and starts the pager's search mode.
- Alt+←,Left and Alt+→,Right move the cursor one word left or right (to the next space or punctuation mark), or moves forward/backward in the directory history if the command line is empty. If the cursor is already at the end of the line, and an autosuggestion is available, Alt+→,Right (or Alt+F) accepts the first word in the suggestion.
- Shift,←,Left and Shift,→,Right move the cursor one word left or right, without stopping on punctuation.
- ↑ (Up) and ↓ (Down) (or Control+P and Control+N for emacs aficionados) search the command history for the previous/next command containing the string that was specified on the commandline before the search was started. If the commandline was empty when the search started, all commands match. See the history section for more information on history searching.
- Alt+↑,Up and Alt+↓,Down search the command history for the previous/next token containing the token under the cursor before the search was started. If the commandline was not on a token when the search started, all tokens match. See the history section for more information on history searching.
- Control+C cancels the entire line.
- Control+D delete one character to the right of the cursor. If the command line is empty, Control+D will exit fish.
- Control+U moves contents from the beginning of line to the cursor to the killring.
- Control+L clears and repaints the screen.
- Control+W moves the previous path component (everything up to the previous "/", ":" or "@") to the killring.
- Control+X copies the current buffer to the system's clipboard, Control+V inserts the clipboard contents.
- Alt+d moves the next word to the killring.
- Alt+h (or F1) shows the manual page for the current command, if one exists.
- Alt+l lists the contents of the current directory, unless the cursor is over a directory argument, in which case the contents of that directory will be listed.
- Alt+p adds the string '| less;' to the end of the job under the cursor. The result is that the output of the command will be paged.
- Alt+w prints a short description of the command under the cursor.
- Alt+e edit the current command line in an external editor. The editor is chosen from the first available of the $VISUAL or $EDITOR variables.
- Alt+v Same as Alt+e.
- Alt+s Prepends sudo to the current commandline.
Toggle class name to .lightMode
to fire light mode.
// by default dark theme
:root {
--bg-color: #171923;
--bg-light: #232535;
--font-color: #c5cddb;
--font-light: #ffffff;
}
// light theme colors
.lightMode {
--bg-color: #e8e6dc;
--bg-light: #dcdaca;
--font-color: #3d3d3d;
--font-light: #202020;
}
˜
for patch version.16.3.x
✔, but16.4.0
❌ˆ
for minor version.16.x.x
✔️, but17.0.0
❌
CRA v4 DO NOT support set path
section in tsconfig.json
, which can set path alias in 'import' statement. (validated on Dec 2020) https://www.typescriptlang.org/tsconfig#paths
Learned from GitHub's repository initial tips: git branch -M main
.
- Fock from https://codesandbox.io/s/node-http-server-node
- Add variable from
Server Control Panel
. https://codesandbox.io/docs/secrets
export const VideoContext = createContext<IVideoContext>(null!); // without 'as'
The slice()
method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included) where begin and end represent the index of items in that array. The original array will not be modified.
- 对于时间、id 等可能为字符串或数值类型的情况,做强转防御。
- 适度抛错,便于 QA 排查。
ng update @angular/cli @angular/core
在 Angular 中,HttpModule
或 Rxjs ajax
方法不需要退订。因为这方法在请求完成后,会自动触发可观察对象的 complete
状态。
思路
以 flatMap
为引导,以理解“高阶可观察者”对象.
https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html
With the following comments: // @ts-nocheck
, // @ts-check
, and // @ts-ignore
.
Heads up: if you have a tsconfig.json
, JS checking will respect strict flags like noImplicitAny
, strictNullChecks
, etc.
行内回调的语句可以拿到 event
对象:
<input onkeyup="peopleStore[1].name = event.target.value" />
https://prettier.io/docs/en/options.html#end-of-line
# .prettierrc
{ "endOfLine": "auto" }
https://docs.expo.io/versions/latest/sdk/safe-area-context/
expo install react-native-safe-area-context
import { SafeAreaView } from "react-native-safe-area-context";
function SomeComponent() {
return (
<SafeAreaView>
<View />
</SafeAreaView>
);
}
https://git-scm.com/book/en/v2/Git-Branching-Rebasing
https://git-scm.com/docs/git-rebase
$ git rebase --onto master server client
GitLens => File History
, right click the target commit hash you wanna restore, selectRestore
. Check the result to make sure it is intending one.git restore <FILE>
, retore it from stages.git add .
, add the changes to stages.git commit --amend --no-edit
, amend the last commit without changing the commit message.
Reason
The actions from both GitLens Restore
and Git git restore
recover the unexpected changes to the original commit. git add . && git commit --amend
will add the desired files to commit again, without the unexpedted ones.
If Yarn is installed with npm install -g yarn
, it's bin
will not be added to Windows Environment Variables by default.
It makes the following error cause:
'parcel' is not recognized as an internal or external command, operable program or batch file.
Howto
yarn global bin
to retrieve yarn global binary bin, and copy the output- Add the path to Windows Environment Variables
https://developer.mozilla.org/en-US/docs/Web/CSS/clamp
https://caniuse.com/css-math-functions
How to use
- Choose a minimum value: E.g. 16px
- Choose a maximum value: E.g. 34px
- Choose a flexible value: E.g. 5vw
h1 {
font-size: clamp(16px, 5vw, 34px);
}
const arr = [1, 5, 2, 7, 0];
const result = arr.sort((a, b) => b - a); // [ 7, 5, 2, 1, 0 ]
(a = 1 b = 5) 2⏳ 7 0 👉 [5, 1, 2, 7, 0]
5 (a = 1 b = 2) 7⏳ 0 👉 [5, 2, 1, 7, 0]
(a = 5 b = 2) 1 7⏳ 0 👉 [5, 2, 1, 7, 0]
5 2 (a = 1 b = 7) 0⏳ 👉 [5, 2, 7, 1, 0]
5 (a = 2 b = 7) 1 0⏳ 👉 [5, 7, 2, 1, 0]
(a = 5 b = 7) 2 1 0⏳ 👉 [7, 5, 2, 1, 0]
7 5 2 (a = 1 b = 0)🏁 👉 [7, 5, 2, 1, 0]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
https://gist.run/?id=da57950803bf00ce555752da6bd7147e&mc_cid=5fa9352dcf&mc_eid=afaf576ce2
Promise.all([asyncFunc1(), asyncFunc2()]).then((results) => {
console.log(results);
});
// OR
const [result1, result2] = await Promise.all([asyncFunc1(), asyncFunc2()]);
const arr = [1, 5, 2, 7, 0] as const 👈;
// Property 'sort' does not exist on type 'readonly [1, 5, 2, 7, 0]'.(2339)
const result = arr.sort((a, b) => b - a)
const fn = async () => {
throw new Error("Error Happened");
};
fn().catch((err) => {
console.table(err);
});
export class X {
id! 👈: string; // JavaScript will not complain: "property id do not exist"
constructor(public age: number = 0) {
}
}
npx tsconfig.json
Library https://callstack.github.io/react-native-testing-library/docs/api
Test case
https://github.com/callstack/react-native-testing-library/tree/master/src/__tests__
https://callstack.github.io/react-native-testing-library/docs/react-navigation
https://callstack.github.io/react-native-testing-library/docs/redux-integration
https://github.com/twilio/twilio-video-app-react
https://www.30secondsofcode.org/blog/s/testing-async-react-components
Recap
- A message about code that causes React state updates not being wrapped in
act(...)
might indicate that a component updated after the test ended. - Using
waitFor()
can solve the issue by making tests asynchronous, but you might need to bump your react-testing-library version if you are using older versions of react-scripts. - If you see errors related to
MutationObserver
, you might need to change your test script to include--env=jsdom-fourteen
as a parameter.
const [updating, setUpdating] = useState(false);
const onRefresh = useCallback(() => {
setUpdating(true);
getLatestBalance(nouAccountId).then((res) => {
setUpdating(false);
});
}, [updating]);
VS Code workspace definition file can define multifolder, which will lead multifolder options while accessing Jest Stop
and setting settings.
List all NPM packages installed globaly.
styled-components/styled-components#1858 (comment)
border-bottom
- yes, but you have to write all styles separately, like border-bottom-width, border-bottom-color, when just border can be written as border: 1px solid #000.box-shadow
- yes, but again, you have to write all styles separately like mentioned here. Also these would work:These would not work (neither with rgb):box-shadow: 2px 4px 12px red; boxshadow: 2px 4px 12px red;
box-shadow: 2px 4px 12px rgba(202, 202, 214, 0.25); boxshadow: 2px 4px 12px rgba(202, 202, 214, 0.25);
https://blog.alispit.tel/create-a-git-diff-in-markdown/
```diff 👈 use "diff" as language indicator
function addTwoNumbers (num1, num2) {
- return 1 + 2
+ return num1 + num2
}