Skip to content

Instantly share code, notes, and snippets.

@hellsan631
Last active June 16, 2021 19:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hellsan631/7ddd45a8e2d3d735d6a7caa344209477 to your computer and use it in GitHub Desktop.
Save hellsan631/7ddd45a8e2d3d735d6a7caa344209477 to your computer and use it in GitHub Desktop.

Component definition

File Structure Example:

// Beginning Of File - imports always at the top
import * as React from 'react'
import { trackEvent } from '@app/lib/AnalyticsUtils'
import UserList from './UserList'

// A lot of other functions and other stuff in the middle of the file. These aren't usually as important
// so finding them immediately isn't an issue.

function doesAThing(): boolean { // Adding a return type can help make the typescript compilation faster.
  return true
}

// include your prop types here, right before the component!.
interface UserSelectionProps {
  initialSelection: Users[];
  users: Users[];
  // Optional properties at the bottom of this list.
  // Functions have `on` appended to them to help easily identify/separate.
  onSelect?: (user: User) => void;
  onClear?: () => void;
  // Prefer boolean to have `is` appendage.
  isMultiSelection?: boolean;
}

/**
 * Some long documentation for your main component...
 *
 * This default export should also be at the end of the file. Much easier to find it this way!
 */
export default function UserSelection({ // one-line: 'export default function' for your main component
  users,
  initialSelection,
  onSelect = () => {},
  onClear = () => {},
  isMultiSelection = false,
}: UserSelectionProps) {
  // Name functions that handle an event with the `handle` prefix.
  const handleSelection = (user: User): void => {
    onSelect(user);
    trackEvent(AnalyticsEvents.SELECT_USER, { userId: user.id })
  }
  return (
    <UserList
      users={users}
      selection={selection}
      onSelect={handleSelection}
      onClear={onClear}
    />
  )
}

Prefer Functional Components

All the components you write should be functional components. Now that hooks are a thing, functional components can store state. They are more compact and easier to understand, render faster than class components, and you can abstract stateful behaviors from them.

// bad
class Listing extends React.Component {
  render() {
    return <div>{this.props.hello}</div>;
  }
}

// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => (
  <div>{hello}</div>
);

// good
function Listing({ hello }) {
  return <div>{hello}</div>;
}

Props

Props from an object been used in the same place should be destructed

export default function SomeComponent({ firstName, lastName }) {
  return <div>{firstName} {lastName}</div>
}

Code style

Use searchable names

// bad - Where does 600 come from? what does this mean in the context ofthe delay function?
delay(doSomething, 600)

// Good - A comment about how we arrived at this measurement is useful!
// This is the time it takes for our parent components on mount spring animation to complete.
const CARD_ANIMATION_DELAY_MS = 600

delay(doSomething, ANIMATION_DELAY_MS)

Naming

Components

Use PascalCase for React components and camelCase for their instances.

// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;

Use the filename as the component name. For example, ReservationCard.js should have a reference name of ReservationCard. However, for root components of a directory, use index.js as the filename and use the directory name as the component name.

// bad
import Footer from './Footer/Footer';

// bad
import Footer from './Footer/index';

// good
import Footer from './Footer';

Props Naming

Avoid using DOM component prop names for different purposes.

Why? People expect props like style and className to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs.

// bad
<MyComponent style="fancy" />

// bad
<MyComponent className="fancy" />

// good
<MyComponent variant="fancy" />

React peculiarities

Prop Names

Always use camelCase for prop names.

// bad
<Foo
  UserName="hello"
  phone_number={12345678}
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
/>

Avoid using an array index as key prop, prefer a unique ID.

  // bad
  {todos.map((todo, index) =>
    <Todo
      key={index}
      {...todo}
    />
  )}

  // good
  {todos.map(todo => (
    <Todo
      key={todo.id}
      {...todo}
    />
  ))}

Avoid Writing Higher Order Components

Hooks provide a better paradigm for encapsulating behavior, as they are basically just functions that can return something. It's much easier to reason about a hook and what it does as compared to a higher order component.

// bad - where does params come from? do I pass in this prop?
function NavLink({ params, label }) {
  // ... logic
}
export default withRouter(NavLink)
// later
<NavLink label="Foo" />

// good - now we know that there is only one prop
function NavLink({ label }) {
  const params = useRouterParams()
  // ... logic
}
<NavLink label="Foo" />

Components

One line props when are 3 or more props.

 // bad - this is pretty hard to read, and proper order could be better.
<button type="submit" disabled onClick={() => null} className="a-long-class-name">
  Click here
</button>

// okay
<button type="submit" className="a-long-class-name" onClick={() => null}>
  Click here
</button>

// good
<button className="a-long-class-name">Click here</button>

// best
<button
  className="long-class-name"
  disabled={loading}
  onClick={() => null}
  type="submit"
>
  Click here
</button>

Inline components should be kept at a minimum.

// bad - try not to nest components inside other components. Prefer to abstract behavior.
function Component({ label, options, onSelect }) {
  const label = (<div>Some Big Long Contrived Example Component {label}</div>);
  const selection = (
    <div>
      {
        options.map((option) => (<div onClick={() => onSelect(option)}>{option.name}</div>))
      }
    </div>
  );
  return <MyComponent>{label}{selection}</MyComponent>;
}

// good, much more separated concerns
function ContrivedExampleBody({ label }) {
  return (<div>Some Big Long Contrived Example Component {label}</div>)
}

function ContrivedExampleSelection({ options, onSelect }) {
  return (
    <div>
      {
        options.map((option) => (<div onClick={() => onSelect(option)}>{option.name}</div>))
      }
    </div>
  )
}

function Component({ label, options, onSelect }) {
  return (
    <MyComponent>
      <ContrivedExampleBody label={label} />
      <ContrivedExampleSelection options={options} onSelect={onSelect} />
    </MyComponent>
  );
}

One line component

// bad - Hard to read, and know what the markup structure is like.
<div className="example"><span class="highlight">Bad</span> example</div>

// good - Structure is more clear
<div className="example">
  <span className="highlight">Good</span> example
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment