The paper contains 7556 words and is expected to last 20 minutes

Source: Pexels


“You’re bald, you’re strong” is the best way to describe programmers.

But is baldness a huge price to pay in order to be strong? Who doesn’t aspire to be a universally loved and technically stunning boy (girl)?


This is the tragedy of all programmers and developers…

Source: Pexels


To this end, the React component is designed to prevent developers from getting bald. We hope it can bring some warmth to programmers who are working on the front line and selfless dedication of their precious hair.


Ok, let’s get started


Designing reusable React components is definitely a challenge, given that people usually work in teams and components are used not only by designers but also by other developers.


Furthermore, if you don’t pay attention to faulty components, you will suffer more if problems occur later. (Small chip here uses the word design, is that design is like the process of continuous in-depth thinking, and then the actual operation, writing code.)


Ideally, take the time to design before you do it.


The following sections discuss some common operations and principles to follow when designing maintainable and developer-friendly React components.


The Context – Free components


When designing reusable components, you need to ensure that components are as scenario-independent as possible.


Like a Button rather than ReportPageConfirmationButton, with a Card instead of UserCard, replace ProfileAvatar with Avatar, and so on. In this way, you can extend the use of components in your project while avoiding the confusion of using components in different scenarios.


For example, from a functional and UI point of view, use within SettingPage ReportPageConfirmationButton might be right, but the semantic aspects will be wrong.


Another advantage is to reduce the abstraction of operations closely tied to a particular use case. Take a look at the Button component below:


functionButton ({ children }) {Copy the code
return <buttondata-testid="login-button">{children}<button/>Copy the code
}Copy the code


The Button component works in any scenario, even if the data-testid only works when logging in, the UI is correct.


But for some reason, this component is bound to the execution of the login button. Therefore, extract the data-testid property as a prop.


functionButton ({ children, dataTestId }) {Copy the code
return <button data-testid={dataTestId}>{children}<button/>Copy the code
}// UsageCopy the code
<Button dataTestId="login-button">Login</Button>Copy the code


Simple and semantically compliant component prop


Use JSX defaults and JavaScript casts


Consider the following Button component:


importReact from 'react';Copy the code
import Loader from '.. /Loader';function Button ({ loading }) {Copy the code
return<button>{ loading ? <Loader /> :'Click me! '}<button/>Copy the code
}Copy the code

Supplied prop defaults to true when it has no value. These two JSX expressions are the same.



<Buttonloading={true} /><Button loading />Copy the code


Not only that, you can also use JavaScript to cast properties. The two JSX expressions are the same.


<Buttonloading={true} /><Button loading />Copy the code


These may not seem like much, but they would work if there were type checking in the React project.


typeTButtonProps = {Copy the code
loading? : BooleanCopy the code
}// So if you do this the type checking won't complainCopy the code
<Button />Copy the code


Also, why is the chip loading instead of isLoading? IsLoadingprop is useful if the value is supplied each time.



<ButtonisLoading={true} /><Button isLoading={false} / >Copy the code


But that’s not the case. We used JSX defaults and JavaScript casts, so the ending would be:



<ButtonisLoading/>Copy the code


This may seem confusing because it is not clear whether the button shows the loader.


//This is much betterCopy the code
<Button loading />Copy the code


Consider component use cases

Source: Pexels


One idea that Chipchip found useful was to design component prop in terms of component use cases.


For example, discuss with the designers on your team and decide that you need a Button that displays the loading status.


In general, there are two options. Execute the LoadingProp or notLoadingprop item.


//WITH LOADING PROPS// Button in normal stateCopy the code
<Button />// Button in loading stateCopy the code
<Button loading/>// WITH NOTLOADING PROPS// Button in normal stateCopy the code
<Button notLoading={false} />// Button in loading stateCopy the code
<Button />Copy the code


Obviously, most Button use cases are not used to show load status. This is best done with loadingProp instead of notLoading.


Otherwise you end up with a lot of implementations in the code base like this:



<ButtonnotLoading={false} / >Copy the code


To practice thinking, what if the Button component executed the other way around?


After talking to the designer, it became clear that another button was needed, and in most cases, this button would show the loading status. We can call this component a LoadingButton.


In most cases, with the same API as the Button component, you need to do:



<LoadingButtonloading />Copy the code


Since it is already clear that most use cases show Loading state, you can change the execution to show a LoadingButton in a Loading state:

//Displaying the loaderCopy the code
<LoadingButton />// Not displaying the loaderCopy the code
<LoadingButton notLoading />Copy the code


The Style Prop is named the same as the CSS properties


Sometimes components are added to improve their CSS properties, preferring to use the same prop name as the CSS property name.


The reason is to make it as simple and easy to understand as possible, because you use CSS a lot as a front-end designer.


For example, if you want to improve a Button component to receive a prop in hexadecimal format, modify its own color and text color.



< Buttoncolor ="# 000 # FFF textColor = "" / >"Copy the code

You can update the component prop name with this instead:



< ButtonbackgroundColor ="Color = "# 000" # FFF "/ >Copy the code
EventHandler PropCopy the code


Add clickability to buttons.

functiononClickHandler = () => console.log('Clicked! ')<ButtononClickButton={onClickHandler}/>Copy the code
// vsCopy the code
<Button onClick={onClickHandler}/>Copy the code

In this case, it is best to use onClickprop instead of onClickButton, and name eventHandler Prop as closely as possible to the event name, because you may need to tie the event to a particular scenario or encounter multiple similar events.


Think of the Modal component with cancel and confirm buttons


Execute onCancel and onConfirm, or onClickCancelButton and onClickConfirmButton.


At this point, the core will favor onCancel and onConfirm because onClick is partially attached to the Button component, is short in length, and does not lose cancel or confirm functionality from the user’s point of view.


//Modal ComponentCopy the code
function Modal ({ children, onCancel, onConfirm }) {Copy the code
return (Copy the code
<div>Copy the code
{children}Copy the code
<ButtononClick={onCancel}>Cancel</Button>Copy the code
<ButtononClick={onConfirm}>Confirm</Button>Copy the code
</div>Copy the code
)Copy the code
}// UsageCopy the code
<ModalCopy the code
onCancel={() =>console.log('Cancelled! ')}Copy the code
onConfirm={() =>console.log('Confirmed! ')}Copy the code
>Copy the code
Are you sure you want to delete thisitem?Copy the code
</Modal>Copy the code

Abstract as much as possible

Source: Pexels


For example, execute the MultiSelect component, which is essentially a list of searchable checkbox options. Only search related options are displayed.


There are two options, option A to perform the search function within the component and option B to transfer the search transfer function to the parent component.


For brevity, you can assume that the Option component has already been executed.


//Option ACopy the code
function MultiSelect ({ options, onSelectOption, searchInputValue, onSearch }){Copy the code
return (Copy the code
<div>Copy the code
<input value={searchInputValue}onChange={onSearch} placeholder="Search here" />Copy the code
<ul>Copy the code
{Copy the code
options.map((option) =><Option value={option.value} label={option.label} onClick={onSelectOption}/>)}Copy the code
</ul>Copy the code
</div>Copy the code
)Copy the code
}// Option A UsageCopy the code
function ParentComponent () {Copy the code
const options = ['Cat'.'Dog'.'Mouse'.'Elephant'] const [ searchInputValue, setSearchInputValue ] = useState(' ');Copy the code
const onSearchHandler = ({ target })=> {Copy the code
setSearchInputValue(target.value)Copy the code
} const [ selectedOptions,setSelectedOptions ] = useState([])Copy the code
// Implement the filterCopy the code
const filteredOptions =options.filter((option) => option.includes(searchInputValue)) return (Copy the code
<MultiSelectCopy the code
options={filteredOptions}Copy the code
searchInputValue={searchInputValue}Copy the code
onSearch={setSearchInputValue}Copy the code
onSelectOptions={setSelectedOptions}Copy the code
>Copy the code
)Copy the code
}Copy the code

Also consider the execution of option B:


//Option BCopy the code
function MultiSelect ({ options, onSelectOption }) {Copy the code
const [ search, setSearch ] =useState(' ');Copy the code
onChangeSearchHandler = ({ target })=> {Copy the code
setSearch(target.value);Copy the code
}const filteredOptions =options.filter((option) => option.includes(search))return (Copy the code
<div>Copy the code
<input value={searchInputValue}onChange={onChangeSearchHandler} placeholder="Search here" />Copy the code
<ul>Copy the code
{Copy the code
filteredOptions.map((option) =><Option value={option.value} label={option.label} onClick={onSelectOption}/>)}Copy the code
</ul>Copy the code
</div>Copy the code
)Copy the code
}// Option B UsageCopy the code
function ParentComponent () {Copy the code
const options = ['Cat'.'Dog'.'Mouse'.'Elephant']const [ selectedOptions, setSelectedOptions ] = useState([])return (Copy the code
<MultiSelectCopy the code
options={options}Copy the code
searchInputValue={searchInputValue}Copy the code
onSearch={setSearchInputValue}Copy the code
onSelectOptions={setSelectedOptions}Copy the code
>Copy the code
)Copy the code
}Copy the code

Execution at ParentComponent is reduced.


It is now assumed that this component will be used in multiple rounds and searches will also be performed. ParentComponent does not need to receive any information about the search operation.


Abstract but customizable components


Abstract and customizable implementations sound relative, but they can go hand in hand.


Modify the internal functions of the component through the modifier function


Hiding execution steps from the user sounds like the opposite of being customizable.


Go back to the MultiSelect component, add another feature, and search through a custom algorithm.


functionMultiSelect ({ options, onSelectOption, searchModifier }) {Copy the code
const [ search, setSearch ] =useState(' ');Copy the code
onChangeSearchHandler = ({ target })=> {Copy the code
setSearch(target.value);Copy the code
}const filteredOptions =options.filter((option) => searchModifier instanceOf Function ? searchModifier(option, search) : option.includes(search))return (Copy the code
<div>Copy the code
<input value={searchInputValue}onChange={onChangeSearchHandler} placeholder="Search here" />Copy the code
<ul>Copy the code
{Copy the code
filteredOptions.map((option) =><Option value={option.value} label={option.label} onClick={onSelectOption}/>)}Copy the code
</ul>Copy the code
</div>Copy the code
)Copy the code
}Copy the code

Custom Render Prop mode for renderer functions


To give the user complete control over how the component is displayed, the Render Prop mode can be used. Consider this operation:


functionMultiSelect ({ options, onSelectOption, searchModifier, children }) {Copy the code
const [ search, setSearch ] =useState(' ');Copy the code
onChangeSearchHandler = ({ target })=> {Copy the code
setSearch(target.value);Copy the code
} const filteredOptions =options.filter((option) => searchModifier instanceOf Function ? searchModifier(option, search) : option.includes(search))if (children instanceOf Function) {Copy the code
return children(filteredOptions,search, onChangeSearchHandler)Copy the code
}return (Copy the code
<div>Copy the code
<input value={searchInputValue}onChange={onChangeSearchHandler} placeholder="Search here" />Copy the code
<ul>Copy the code
{Copy the code
filteredOptions.map((option) =><Option value={option.value} label={option.label} onClick={onSelectOption}/>)}Copy the code
</ul>Copy the code
</div>Copy the code
)Copy the code
}// UsageCopy the code
function ParentComponent() {Copy the code
return (Copy the code
<MultiSelect>Copy the code
{Copy the code
(options, searchValue,onChangeSearchHandler) => {Copy the code
returnCopy the code
<div>Copy the code
<input value={searchValue}onChange={onChangeSearchHandler}/>Copy the code
<span>Here are the optionlist! </span>Copy the code
{Copy the code
options.map(option =><span>{option}</span)Copy the code
}Copy the code
</div> }Copy the code
</MultiSelect> )Copy the code
}Copy the code

If the goal is to be more precise, you can perform specific render on the specified part of the MultiSelect component. For example, searchInputRenderer allows users to control what is entered in a search.


functionMultiSelect ({ options, onSelectOption, searchModifier, children,searchInputRenderer }) {Copy the code
. return (Copy the code
<div>Copy the code
{Copy the code
searchInputRenderer instanceOfFunction ?Copy the code
searchInputRenderer(searchInputValue,onChangeSearchHandler):Copy the code
<input value={searchInputValue}onChange={setSearch} placeholder="Search here" />Copy the code
}Copy the code
<ul>Copy the code
{Copy the code
filteredOptions.map((option) =><Option value={option.value} label={option.label} onClick={onSelectOption}/>)}Copy the code
</ul>Copy the code
</div>Copy the code
)}Copy the code

Read so much, I don’t know if you have learned ~

Leave a comment like follow

We share the dry goods of AI learning and development. Welcome to pay attention to the “core reading technology” of AI vertical we-media on the whole platform.



(Add wechat: DXSXBB, join readers’ circle and discuss the freshest artificial intelligence technology.)