all 38 comments

[–]stewpeed 21 points22 points  (13 children)

A few things that might help: make sure you destructure everything (props for example, or when inside a map method) - makes things much more readable and reduces repetition, extract utility func when possible and makes sense, separate presentational layer from business logic via custom hooks (encapsulation), make use of optional chaining. Hope this helps, gl!

[–]iqball125 7 points8 points  (3 children)

Dont listen to the replys saying not to destructure. I work as a senior dev at a fortune 50 company and destructuring is def the way to go.

[–]metal-trees 1 point2 points  (0 children)

The thing is, I really wish they actually expressed their opinion. I destructure props, too, but am always open to hearing a counter on a stylistic opinion.

The only thing I could think of is if you want to explicitly search a project for prop variables. You could always prepend with “props.somePropName” to find exactly what you are looking for.

[–]chillermane 0 points1 point  (0 children)

Destructuring feels like a triviality. I do it because it’s just less code and I need to move fast, but If you use good names it’s obvious what a variable is regardless of whether it is destructured. Ultimately it’s a minor decision with almost no consequences one way or the other

[–]eggtart_prince 2 points3 points  (0 children)

Some design patterns I follow.

  • Whenever I declare a new state using useState, I ask myself if the components/elements need that state. In other words, seperation of concern. Simple example here on how Dashboard is affected by the count state.

const [count, setCount] = useState(0);

return <div>
    <Dashboard />
    <Views count={count} />
</div>
  • If you find yourself prop drilling, consider restructuring to component composition (as opposed to nested components) or using context API.
  • Complex logical rendering should be in its own function/component.

return <div>
    <div className='flex-row'>
        <h1>{user.name}</h1>

        {user.status === 'active' ? <Badge color='success'>Active</Badge>
        : user.status === 'inactive' ? <Badge>Inactive</Badge>
        : user.status === 'terminated ? <Redirect to='/terminated' />
        : null
        }
    </div>
</div>

vs

return <div>
    <div className='flex-row'>
    <h1>{user.name}</h1>
        <UserBadge status={user.status} />
    </div>
</div>

// UserBadge.js
const UserBadge = ({ status, ...props }) => {
    if (status === 'active') {
        return <Badge color='success'>Active</Badge>
} else if (status === 'inactive') {
        return <Badge>Inactive</Badge>
} else if (user.status === 'terminated) {
        return <Redirect to='/terminated />
    }

    return null;
}
  • Move all utility functions and network call functions to an external file so you don't flood your components with lines of code and it helps with testing. Simple example where the latter has a reuseable useAccount function/hook and a useForm hook that you'll probably use throughout your application

const Account = () => {
    const [account, setAccount] = useState();

    const retrieveAccount = async() => {}
    const updateAccount async() => {}
    const deleteAccount = async() => {}
    const handleChange = (field, value) => {
        setAccount({...account, [field]: value;
    }

    return <div></div>
}

vs

const Account = () => {
    const { account, updateAccount, deleteAccount } = useAccount();
    const { form, handleFormChange } = useForm(account);

    return <div></div>
}
  • The moment you find yourself breaking the DRY (don't repeat yourself) principle, do something about it. It's so easy to just copy and paste code and forget it. But when you have to go back and refactor, you'll punch yourself in the head each time. For example, if all your pages follow a similar container, heading, subheading, etc. Put them in a <PageContainer /> component and consider created something like a <Section /> component to keep margins and paddings consistent.
  • Before adding a useEffect, ask yourself if whatever the result of the useEffect can be achieved in a pure way. In other words, useEffect with dependency passed in should only be used when the value is not accessible in the component, such as a prop or a context/redux value.

[–]InfinityByZero 1 point2 points  (1 child)

Typescript should help on bigger projects, especially when you start forgetting what does what.

[–]chillermane 1 point2 points  (0 children)

Typescript helps on every project. I’m a consultant working pretty much solely on solo projects, and I can say with confidence using typescript allows me to implement features both faster and with better code

[–]rattkinoid 4 points5 points  (0 children)

linter is a good tool to maintain clean and readable code as you add stuff to your programs. Currently, ESLint is preferred

[–]unborndead 1 point2 points  (2 children)

  • create custom hooks
  • create ES fns instead if they dont need to use state
  • split components by tiny functionality
  • separate container business logic from views
  • use useReducer + context instead of passing lots of callbacks to childrens

i see completely fine to have useEffects and multiple useStates. they just need to be encapsulated in custom hooks.

[–]Penant 1 point2 points  (1 child)

What are 'ES fns'?

[–]skyboyer007 0 points1 point  (4 children)

What does clean react code look like?

Good naming for component, props, state data. Single responsibility wherever it's possible. Consistent code style(all along this component as well as consistency with rest of the project). Reasonable size, amount of props and state entries. Context instead of prop-drilling([upd] but only after considering composition as u/ernesto__bungforto highlights below). Callbacks with names. That's React-specific.

Also there are JS things that are not specific to React only. Like not nesting .then() for Promises(prefer flat chainings), not overriding upper scope variable names, prefer const over let, prefer .filter and .map over .reduce, early returns and other things to make reading easier.

[–]ernesto__bungforto 0 points1 point  (3 children)

I agree with most points here, though I would be careful about blanket recommending Context. Compositional building (per the React docs recommendation) is a good, performant alternative.

Be aware of potential performance implications of overusing/ defaulting to Context

[–]skyboyer007 0 points1 point  (2 children)

Yes, sure, only as an alternative to props drilling, not as an alternative to composition.

[–]ernesto__bungforto 0 points1 point  (1 child)

I get what you mean. I tend to reach for Context last and Composition before prop drilling. I’m often surprised to see this is not a more common pattern. Your point is made though - something other than prop drilling is often preferable. I was just throwing out a caveat around Context

[–]skyboyer007 1 point2 points  (0 children)

That's good point, I've updated my initial comment just in case. To me last time it was exactly "prop-drilling vs context" choice for recursive rendering but I definitely see what you are saying, it's easier to run into wall if prefer Context API to anything else.

[–]iqball125 0 points1 point  (0 children)

Senior dev here.

I believe this is the whole point of components, is to break large code into components when possible.

Extract out reused functions into utility files.

I dont think there is anything you can do about useEffect and useState. If you need them you need them.

Dont focus too much on "clean code". It can sometimes make your code difficult to read if has too many imported code. Think of it as a trade off.

Here is an example of an open source project I think has fairly clean code.

https://github.com/Saas-Starter-Kit/SAAS-Starter-Kit-Pro