Espen Henriksen
Front-end team lead
Oslo Market Solutions
espen_dev
esphen
Best practices in React and friends
Let's learn from each other!
Let's go
Why are we here?
- We're all in this together
- We make each other better
- We're a tiny company, everyone is an essential part
- We need to learn from each other
- It's everyone's responsibility to spread their knowledge
Agenda
- Show some anonymized code examples
- Don't fret if you see code you wrote
- Check your ego at the door
- Be pragmatic
- Think of it as a great opportunity to learn
Scrollbars
Click this
Click surfaces
Temporary variables
function FooComponent({ loggedIn }) {
let sidebarContent;
if (loggedIn) {
sidebarContent = (
<div>
I'm sorry Dave, I'm afraid I can't do that
</div>
);
}
if (!loggedIn) {
sidebarContent = (
<div>
Welcome
</div>
);
}
return (
<aside>
{sidebarContent}
</aside>
);
}
Solution: Inline conditionals
const FooComponent = ({ loggedIn }) => (
<aside>
{loggedIn && (
<div>
I'm sorry Dave, I'm afraid I can't do that
</div>
)}
{!loggedIn && (
<div>
Welcome
</div>
)}
</aside>
);
Solution: Functions
const FooComponent = ({ loggedIn }) => (
<aside>
{loggedIn && renderLoggedInView()}
{!loggedIn && <LoggedOutMessage />}
</aside>
);
Static jsx in dynamic syntax
const FooComponent = () => (
<Link to={'/trader/news'}>
To the place
</Link>
);
Solution: Don't
const FooComponent = () => (
<Link to="/trader/news">
To the place
</Link>
);
String concated children
const FooComponent = ({ values: { SECTOR, LONG_NAME }}) => (
<h2>
{`${SECTOR}: ${LONG_NAME}`}
</h2>
);
Solution: Jsx syntax
const FooComponent = ({ values: { SECTOR, LONG_NAME }}) => (
<h2>
{SECTOR}: {LONG_NAME}
</h2>
);
Unclear intentions
const FooComponent = ({ small, location }) => {
const { page } = qs.parse(location?.search, {
ignoreQueryPrefix: true,
});
return (
<Switch location={small && page ? { pathname: `/${page}` } : location}>
...
</Switch>
);
};
Solution: Named constants
const FooComponent = ({ small, location }) => {
const urlQuery = qs.parse(location?.search, {
ignoreQueryPrefix: true,
});
const isOnMobile = small && urlQuery.page;
const currentLocation = isOnMobile
? { pathname: `/${urlQuery.page}` }
: location;
return (
<Switch location={currentLocation}>
...
</Switch>
);
};
Solution: Component
const ResponsiveSwitch = ({ small, location, children }) => {
const urlQuery = qs.parse(location?.search, {
ignoreQueryPrefix: true,
});
const isOnMobile = small && urlQuery.page;
const currentLocation = isOnMobile ? { pathname: `/${page}` } : location;
return (
<Switch location={currentLocation}>
{children}
</Switch>
);
};
const FooComponent = () => (
<ResponsiveSwitch>
...
</ResponsiveSwitch>
);
Context consumer
const FooComponent = () => (
<FooContext>
{({ fooValue }) => (
<p>
{fooValue}
</p>
)}
</FooContext>
);
Solution: useContext hook
const FooComponent = () => {
const { fooValue } = useContext(FooContext);
return (
<p>
{fooValue}
</p>
);
);
Solution: Custom hook
export const useFoo = () => {
return useContext(FooContext);
};
const FooComponent = () => {
const { fooValue } = useFoo();
return (
<p>
{fooValue}
</p>
);
);
Misleading variables
const FooComponent = () => {
const isLoggedIn = 'Espen';
return (
<p>
{isLoggedIn}
</p>
);
};
Misleading variables
const FooComponent = ({ isLoggedIn }) => {
if (!isLoggedIn) return <LoggedOutMessage />;
return (
<p>
Welcome!
</p>
);
};
Solution: Intuitive naming
const FooComponent = ({ loggedInUser }) => (
<p>
Welcome {loggedInUser.name}!
</p>
);
Chaining
const array = [...];
const filteredArray = array.filter(...);
const sortedArray = filteredArray.sort(...);
const mappedArray = sortedArray.map(...);
const flattenedArray = mappedArray.flat(2);
Chaining
const array = [...];
const users = array
.filter(...)
.map(...)
.flat(2);
const sortedUsers = users.sort(...);
Chaining
const FooComponent = ({ users }) => (
<ul>
{users
.filter(...)
.map(...)
.flat(2)
.sort(...)
.map(...)}
</ul>
);
Chaining
const filterUsers = users => (
users
.filter(...)
.map(...)
.flat(2)
.sort(...)
);
const FooComponent = ({ users }) => (
<ul>
{filterUsers(users).map(...)}
</ul>
);
Similar components
const FooComponent = ({ type }) => (
<div>
{type === 'user' && <UserTable />}
{type === 'admin' && <AdminTable />}
</div>
);
Solution: generic component
const FooComponent = ({ type }) => (
<>
<Table type="user" />
<Table type="admin" />
</>
);
const Table = ({ type }) => (
<table>
<thead>
<tr>
<th>{type === 'user' ? 'Brukernavn' : 'Admin-navn'}</th>
...
</tr>
</thead>
...
</table>
);
Similar components
import request from 'superagent';
request
.get('https://vg.no')
.then(function(response) {
console.log(response.body);
});
Solution: Async fetch
async function() {
const response = await fetch('https://vg.no');
console.log(await response.json());
}
Commit
$ git commit -am "ABC-123 ABC-124 ABC-125 Fix bugs"
Solution: Separate commits
$ git add file1
$ git commit -m "ABC-123 Solves button responsiveness"
$ git add file2
$ git commit -m "ABC-124 Fixes link target"
$ git add file3
$ git commit -m "ABC-125 Upgrades moment version to 3.2"
Tip: Detailed commits
$ git add file1
$ git commit -m "ABC-123 Fixes performance issues on portfolio page
This happened because iPhones expose a special API called
Steve that makes it overheat when exposed to bad design.
Fixed by using the newer Tim API which doesn't seem to care"
Commiting commented code
<label
htmlFor="file"
className={`file ${this.checkValidationErrors('date')}`}
>
Fil:
<input
className={styles.file}
type="file"
name="file"
accept=".pdf"
placeholder="Klikk for å velge fil"
onChange={this.handleFileChange}
/>
{/* <FileInput name="file"
accept=".pdf"
placeholder="Klikk for å velge fil"
onChange={ this.handleFileChange }
/> */}
</label>
Solution: Delete it
It's in the git history
In summary
In summary
- Writing maintainable (readable) code Α+Ω
- Take your time - ensure the code you wrote is bug-free
- When you run across unmaintainable or buggy code, fix it
- When you run across code you don't understand, challenge yourself to understand it - you may learn something
Takeaways
- Read other's code and try to learn from them
- Be critical of your own code, "kill your darlings"
- When in doubt, ask colleagues if they know any better solutions
- Merge requests
- Never stop being curious
- Spread your knowledge
- But don't confront anyone
- Finally: What do you think about this format?
Fin
Let's talk
https://slides.com/esphen/react-patterns
Best practices in React and friends
By Eline H
Best practices in React and friends
- 29