(without losing quality)
D.R.Y. (Don’t Repeat Yourself)
K.I.S.S. (Keep It Simple, Stupid)
Y.A.G.N.I. (You Aren't Gonna Need It)
"A class should only have a single responsibility, that is, only changes to one part of the software’s specification should be able to affect the specification of the class."
"A class should only have a single responsibility, that is, only changes to one part of the software’s specification should be able to affect the specification of the class."
import React, { useState } from 'react'
import { Box, Button } from '@chakra-ui/core'
function PresentationalComponent() {
const [someState, setSomeState] = useState(false)
const onSubmit = async () => {
setSomeState(true)
await fetch('https://api.com')
setSomeState(false)
}
if (someState) {
return (
<Box mt={1} backgroundColor="red">
Loading
</Box>
)
}
return (
<div>
Some view <Button onClick={onSubmit}>Submit</Button>
</div>
)
}
export default PresentationalComponent
import React, { useState } from 'react'
import { Box, Button } from '@chakra-ui/core'
// Loading.tsx
const Loading = () => (
<Box mt={1} backgroundColor="red">
Loading
</Box>
)
// MyView.tsx
const MyView = ({ onSubmit }) => (
<div>
Some view <Button onClick={onSubmit}>Submit</Button>
</div>
)
// useMyView.ts
function useMyView() {
const [someState, setSomeState] = useState(false)
const onSubmit = async () => {
setSomeState(true)
await fetch('https://api.com')
setSomeState(false)
}
return { onSubmit, someState }
}
// PresentationalComponent.tsx
function PresentationalComponent() {
const { onSubmit, someState } = useMyView()
if (someState) {
return <Loading />
}
return <MyView onSubmit={onSubmit} />
}
export default PresentationalComponent
"Software entities … should be open for extension, but closed for modification."
function CustomInput({ icon = 'user' }) {
const icons = {
user: <i className="fas fa-user"></i>,
}
return (
<div>
<input type="text" />
{icons[icon]}
</div>
)
}
export default CustomInput
type Props = {
icon: ReactNode,
}
function CustomInput({ icon }: Props) {
return (
<div>
<input type="text" />
{icon}
</div>
)
}
export default CustomInput
// MyView.tsx
import { FaBeer } from 'react-icons/fa';
import { DiApple } from 'react-icons/di';
import { MdArrowCircleRight } from 'react-icons/md';
<CustomInput icon={<FaBeer />} />
<CustomInput icon={<DiApple />} />
<CustomInput icon={<MdArrowCircleRight />} />
"Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program."
import { useForm } from 'react-hook-form'
function FormA({ onSubmit }) {
return (
<form onSubmit={onSubmit}>
<input name="email" type="email" />
<input name="password" type="password" />
</form>
)
}
function FormB({ onSubmit }) {
const { handleSubmit, register } = useForm()
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="email" type="email" {...register("email")} />
<input name="password" type="password" {...register("password")} />
</form>
)
}
const MyView = () => (
<>
<FormA onSubmit={console.log} />
<FormB onSubmit={console.log} />
</>
)
import { useState } from 'React'
import { useForm } from 'react-hook-form'
function FormA({ onSubmit }) {
const [form, setForm] = useState({})
const handleSubmit = (event) => {
event.preventDefault()
onSubmit(form)
}
const handleChange = (name) => (event) =>
setForm({ ...form, [name]: event.target.value })
return (
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
onChange={handleChange('email')}
value={form.email}
/>
<input
name="password"
type="password"
onChange={handleChange('password')}
value={form.password}
/>
</form>
)
}
// ...
import { useState } from 'React'
import { useForm } from 'react-hook-form'
// ...
function FormB({ onSubmit }) {
const { handleSubmit, register } = useForm()
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="email" type="email"
{...register("email")}
/>
<input name="password" type="password"
{...register("password")}
/>
</form>
)
}
function MyView() {
return (
<>
<FormA onSubmit={console.log} />
<FormB onSubmit={console.log} />
</>
)
}
"Many client-specific interfaces are better than one general-purpose interface."
import { Avatar } from '@chakra-ui/core'
type User = {
name: string
age: number
email: string
photoURL: string
}
function UserAvatar({ name, photoURL }: Pick<User, 'name' | 'photoURL'>) {
return <Avatar name={name} src={photoURL} />
}
function UserInput({ name }: Pick<User, 'name'>) {
return <Input placeholder={name} />
}
import { Avatar } from '@chakra-ui/core'
type Props = {
name: string
photoURL: string
}
function StyledAvatar({ name, photoURL }: Props) {
return <Avatar name={name} src={photoURL} />
}
function MyView() {
return <Input placeholder={user.name} />
}
One should “depend upon abstractions, [not] concretions.”
Concretions
import { useForm } from 'react-hook-form'
function MyView() {
const { handleSubmit, register } = useForm()
const onSubmit = async (values) => {
await axios.post('/api/login', values)
}
return (
<Container>
<form onSubmit={handleSubmit(onSubmit)}>
<input name="email" type="email" {...register('email')} />
<input name="password" type="password" {...register('password')} />
</form>
</Container>
)
}
// AuthService.tsx
class AuthService {
login({ email, password }): Promise<void> {
if (!email || !password) {
throw new Error('Email and password are required')
}
return axios.post('/api/login', values)
}
}
export default new AuthService()
// LoginForm.tsx
import { useForm } from 'react-hook-form'
function LoginForm({ onSubmit }) {
const { handleSubmit, register } = useForm()
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="email" type="email"
{...register('email')}
/>
<input name="password" type="password"
{...register('password')}
/>
</form>
)
}
// MyView.tsx
function MyView() {
const onSubmit = async (values) => {
// bonus tip: don't hide what's inside
// "values" object. Show it to the world
// for better debugging/readability
const { email, password } = values
await AuthService.login({ email, password })
}
return (
<Container>
<LoginForm onSubmit={onSubmit} />
</Container>
)
}
Don't Repeat Yourself (It ain't obvious!)
import { Button } from '@chakra-ui/core'
function MyView() {
return (
<Button variant={'brand'} size={'sm'} width={'200px'} fontSize="12px">
Submit
</Button>
)
}
import { Button } from '@chakra-ui/core'
function MyView() {
return (
<Button variant={'brand'} size={'sm'}>
Submit
</Button>
)
}
// theme/components/button.ts
export const buttonStyles = {
components: {
Button: {
variants: {
brand: () => ({
fontSize: '12px',
}),
},
},
},
}