Here are some reading notes from Michele Bertoli’s React Design Patterns and Best Practices.


Talk is cheap, just show me the code.

Blah, blah, blah, blah, blah, blah, blah.

About conditional judgments in the Render function

In React, there is a situation where we often need to make conditional decisions about whether to render certain components. Something like this:

<div>
    { isLoggedIn ? <LogoutButton /> : <LoginButton /> }
    { visible && <Modal /> }
</div>
Copy the code

For more complex requests, we can use methods and computed attributes instead of trinary and and or judgments.

handleShowLoginButton() {
    return this.isLoggedIn && this.isAuthed;
}
get getVisible() {
    return this.visible && this.displayMode === "normal"
}

render() {
    return (<div>
        { handleShowLoginButton() ? <LogoutButton /> : <LoginButton /> }
        { getVisible && <Modal /> }
    </div>)}Copy the code

Then came the hack, when we wanted to take the judgment logic out of the render function so that the render function was only responsible for rendering. We need to use render-if render-only-if JSX-Control-statements helper dependencies. The guest officer looked at:

const isShowLoginButton = renderIf(
    this.isLoggedIn && this.isAuthed
)
return (<div>
    { isShowLoginButton(<LoginButton />} {/* renderIf of isShowLogoutButton is not displayable */}</div>)
Copy the code

Then render-only-if is essentially a higher-order function, which is formally more elegant than render-if.

const LoginButtonOnlyIf = onlyIf(
    ({ isLoggedIn && isAuthed }) = > {
        return isLoggedIn && isAuthed
    }
)(LoginButton)

return (
    <LoginButtonOnlyIf 
        isLoggedIn={isLoggedIn}
        isAuthed={isAuthed}
    />
)
Copy the code

Conclusion:

  • If it’s just a simple conditional judgment, the ternary and and or operators already satisfy most people’s needs.
  • RenderIf is a good idea if you want to separate concerns;
  • Finally, if you want your judgment logic to be reusable (as if you need logon status and user permissions for multiple components on multiple pages), you can use onlyIf to build reusable higher-order components within your project.

Then let’s finally look at the use of the disgusting jsX-Control-statements:

<If condition={this.handleShowLoginButton}>
    <LoginButton />
</If>

<When condition={this.handleShowLoginButton}>
    <LoginButton /> 
</When>
<When condition={! this.handleShowLoginButton}>
    <LogoutButton />// => < span style = "box-sizing: border-box! Important</When>
<Otherwise>
    <p>oops.. no condition matched.</p>
</Otherwise>


<ul>
    <For each="resultItem" of={this.resultList}>
        <li>{resultItem.name}</li>
    </For> 
    // => {resultList.map(resultItem => <li>{resultItem.name}</li>)}
</ul>
Copy the code

Develop reusable components

This is about performance and maintainability.

Always keep in mind that setting state triggers component rerendering. Therefore, only the values used by the rendering method should be stored in the state.

Here are steps created by Dan Abramov (I don’t know who he is) to help us make the right status choices:

function shouldIKeepSomethingInReactState() {
    if (canICalculateItFromProps()) {
        // Don't use the props attribute directly in the state state,
        // Use them directly in the render() function
        return false
    }
    if(! amIUsingItInRenderMethod()) {// Do not put data in state that is not involved in rendering.
        // In other words, only data that needs to be involved in the component's render update is placed in state
        return false
    }
    // State can be used in all cases except the above.
    return true;
}
Copy the code

For prop type validation, React provides the component’s propTypes attribute to use:

const Button = ({text}) => <button>{text}</button>

Button.propTypes = {
    text: React.PropTypes.string
}
Copy the code

In the TypeScript world, however, we can declare prop interfaces for React components using template classes:

interface IButtonProps = {
    text: string;
}

class ButtonClass extend React.Component<IButtonProps.IButtonStates> {}
// => The state property can also be checked
Copy the code

Next, document the components automatically, using the react-Docgen tool.

import React from 'react'; /** * Sheet component */ const Sheet = ({title}) => <div>{title}</div> Sheet. Prototype = {/** * Sheet title */ title: React.PropTypes.string }Copy the code

Run the react-Docgen sheet.js command to generate the following JSON description:

{
    "description": "Sheet components"."displayName": "Sheet"."methods": []."props": {
        "title": {
            "type": {
                "name": "string"
            },
            "required": false."description": "Sheet title"}}}Copy the code

Using this JSON file as input to the team’s front-end documentation project, you can automatically generate usable component documentation that says la la la.

All right, storybook is the biggest game in the industry.

npm i --save @kadira/react-storybook-addon
Copy the code

(@kadira/ react-storybook-Addon has been scrapped.)

The story document is placed in the Stories folder, where we create sheet.js to define the story document for the component we defined above.

// => stories/sheet.js
import React from 'react';
import Sheet from '.. /src/components/Sheet';
import { storiesOf } from '@kadira/storybook'

storiesOf('Sheet'.module)
    .add('The story of the Sheet without the title attribute.. ', () = > (<Sheet/>
    ))
Copy the code

But to write the story, we need to configure the storybook in the root directory:

// =>.storybook/config.js => create.storybook folder in the root directory
import { configure } from '@kadira/storybook';

function loadStories() {
    require('.. /src/stories/sheet')
}

configure(loadStories, module)
Copy the code

Finally we add script to our package.json to run our story.

    "storybook": "start-storybook -p 9001"
Copy the code

After running, you can see the story file on port 9001.


Let’s go into the component thing a little bit more

I won’t talk about container components and fool components here. After all, it is the primary content. We went straight to the point.

For example, the simplest way to implement a higher-order component is to add a class name to the component:

const withClassName = Component= > props => (
    <Component {. props} className="my-class" />
)
Copy the code

We can move any component property except prop.

const withTimer = Component= > (
    class extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                timer: null
            }
        }
        componentDidMount() {
            this.timer = setTimeInterval((a)= > {
                console.log('Print logs every 1.5 seconds hahaha')},1500)
        }
        componentWillUnmount() {
            clearInterval(this.timer)
        }
        render() {
            // => pass the received props to Component as is
            // State passing Compnent is optional, depending on the actual requirements
            return <Component {. this.props} {. this.state} / >Const SheetWithTimer = withTimer(Sheet); const SheetWithTimer = withTimer(Sheet);Copy the code

The Recompose library already provides some advanced components for useful scenarios, right out of the box.

Now let’s get a little gimmicky and see how function subcomponents play. First of all, the withClassName is so low that className is dead! ? Do not write anything dead, demand later minutes to change for you.

Obviously, we need to do one more layer of logic in the withClassName component, and then dynamically pass the className to the child component. At this point, for the sake of gimmicks, we decided to use the function subcomponent model.

const withClassName = ({children}) = > children('my-class'); // => WFT ('my-class');

<withClassName>
    {(classname) => <Component className={classname} />}
</withClassName>
Copy the code

Then, we see the possibilities… Now, withClassName is still stateless, but we can add life hooks and function methods and states just like the withTimer component. Then in render the difference is (instead of using <Component /> directly, we call children()) :


render() {
    return <Component {. props} / >Renturn {children(props)} //Copy the code

Perhaps a better example is the scenario where a higher-order component needs to make an HTTP request, passing the requested data back to the child component for rendering.

<Fetch url="...">
    {data => <MyComp data={data} />}
</Fetch>
Copy the code

Finally, let’s talk about CSS

First, we can simply write style in an HTML element, which in the JSX world looks like this:

<div style={{ fonSize: this.state.fontSize }} />
Copy the code

However, the downside of the style inline style is that you can’t write media queries and pseudo-elements directly, nor can you write animations.

So Radium came into being.

With Radium, you can arbitrarily operate like this:

import radium from 'radium'

const myStyle = {
    fontSize: '12px'.':hover': {
        color: '#abc'
    },
    '@media (min-width: 720px)': {
        color: '# 121212'}}const Div = (a)= > <div style={myStyle} />

export default radium(Div);
Copy the code

Of course, to use media query you also need to preserve a styleroot element in the outermost layer, otherwise Radium will not know where your media query root element is.

import { StyleRoot } from 'radium'

class App extends Component {
    render() {
        return (
            <StyleRoot>
                <router-view />
            </StyleRoot>)}}Copy the code

Instead of using inline styles, use classname-based CSS modules.

import styles from './index.less'

render() {
    return {
        <div className={styles.myDiv} />
    }
}
Copy the code
/* index.less */
.myDiv {
    font-size: 12px
}

/* The default module generates a bunch of unreadable English class names. If you want to make the class name global instead of local, you can use :global */
:global .myGlobalDiv {
    font-size: 15px
}

/* We can also use composes to add styles from other classes */
.myDivCop {
    composes: .myDiv;
    color: '# 101010'
}
Copy the code

Also, if you don’t want to write your className as style.[className] but as a string className, you can use the react-css-modules library.

Then use this position:

import cssModules from 'react-css-modules'
import styles from './index.less'

class DivComp extends Component {
    render() {
        return (
            <div className='myDiv' />
        )
    }
}

export cssModules(DivComp, styles)
Copy the code

And, for the style network, there is also a styled components, which may be the future trend, and will not be described here as styled components.


Scan the code and tell me what you want to read next (Cute face)

Just kidding, please let me know in the comments section what you want to read. I will only choose what I know and not choose anything hahaha I will try my best to spare time for the demo and read more. You are also welcome to pay attention to urge me to write more next time.