My opinion on TypeScript, beginning of 2021 🔗
- #typescript
- #javascript
It sucks.
It sucks.
While working on Fluor.js's website, I found myself needing to reload node modules when in watch mode in order to update components used by the generated pages (I'm using Server Side Rendering with React to build the website).
Here's the snippet I used to recursively invalidate a module's cache entry and all its dependencies:
function removeFromCache(moduleId) {
const cached = require.cache[moduleId]
if (!cached) {
return
}
// LIB_ROOT and SITE_ROOT are where my source files reside. I avoid reloading
// node_modules as it causes some issues with the rendering process
const ownModule = (mod) =>
mod.path.startsWith(LIB_ROOT) || mod.path.startsWith(SITE_ROOT)
cached.children.filter(ownModule).forEach((mod) => removeFromCache(mod.id))
delete require.cache[moduleId]
}
I recently discovered TailwindCSS and I think it's a great tool to be able to quickly build fancy websites without spending too much time fiddling with the CSS.
I came up with a five-line React component that let's you write
styled-components
-like components using Tailwind class names:
import React from "react"
import clsx from "clsx"
export default (Component, ...tailwindStyles) => ({ className, ...props }) => (
<Component className={clsx(...tailwindStyles, className)} {...props} />
)
(I know, that's actually 6 lines with the blank one 😏)
Usage is very simple as well:
const Warning = styled("div", [
"my-4",
"p-4",
"bg-orange-100",
"border",
"border-orange-300",
"text-orange-800",
"text-sm",
"rounded-lg",
"flex",
])
Bonus points for the regular syntax instead of the funky (read: ugly) mix of
template strings and CSS that you're used to with styled-components
.
EDIT a few hours later: I realized I was missing the ability to derive
styles from props
, so I added it!
import React from "react"
import clsx from "clsx"
function isFunction(object) {
return Boolean(object && object.constructor && object.call && object.apply)
}
export default (Component, ...tailwindStyles) => ({ className, ...props }) => {
const resolvedStyles = tailwindStyles.map((style) =>
isFunction(style) ? style(props) : style
)
return <Component className={clsx(...resolvedStyles, className)} {...props} />
}
// Example usage:
export default styled(
"button",
(props) => [
"rounded",
"py-3",
"px-4",
"font-bold",
"shadow-md",
"leading-none",
"focus:outline-none",
"focus:shadow-outline",
props.disabled ? "bg-green-200" : "bg-green-400",
props.disabled ? "text-green-500" : "text-blue-900",
],
(props) => ({
"hover:bg-green-300": !props.disabled,
"hover:text-green-800": !props.disabled,
"cursor-not-allowed": props.disabled,
})
)
Update March 2020: Use useReducer!
Redux is probably the most used state management library for React. However, even its own creator has trouble using it, probably because it is one massive piece of bloat and boilerplate.
Reading some Redux example code I wrote four years ago and I have no effing idea what's going on there
— Dan Abramov (@dan_abramov) November 4, 2019
I used to solve that problem using elfi, a dead simple state management library that I wrote a few years ago. Elfi is a 50 LOC lib that does not carry any difficult to grasp concepts like reducers, thunks or whatever, but instead only relies on functions.
But given the recent additions to React, even elfi is useless now. Using React Contexts, I simply implement state management in my top level component and pass the state management functions through the context:
// AppContext.js
export const AppContext = React.createContext()
// App.js
function App() {
// The global application state. We use an object here but an
// immutable value is also a pretty solid choice
const [state, setState] = useState({ counter: 0 })
// The dispatch function updates our state by applying a given
// function on the previous state
const dispatch = (fn, ...args) => setState(fn(state, ...args))
return (
<AppContext.Provider value=>
<Button />
</AppContext.Provider>
)
}
// Button.js
function Button() {
const { state, dispatch } = useContext(AppContext)
return <button onClick={() => dispatch(increment)}>{state.counter}</button>
}
function increment(state) {
return { ...state, counter: state.counter + 1 }
}