In the React world, “Everything is a component.” Components can be mapped as functions in functional programming. The flexible features of React components and functions can be used not only to draw UI, but also to encapsulate business state and logic, or to display irrelevant side effects, and then compose complex applications. This article attempts to explain common business development scenarios using the React component thinking.
Series directory
- 01 Type Check
- 02 Component organization
- 03 Style management
- 04 Component thinking
- 05 Status Management
directory
- 1. Advanced components
- 2. Render Props
- 3. Use components to abstract business logic
- 4. Hooks replace higher-order components
- 5. Hooks
responsive
programming - 6. Class inheritance is also useful
- 7. Mode box management
- 8. Use Context for dependency injection
- 9. An immutable state
- 10. React-router: URL is state
- 11. Component specifications
- extension
1. Advanced components
For a long time, higher-order components were the most popular way to enhance and combine React components. This concept is derived from the higher-order functions of functional programming. A higher-order component can be defined as: A higher-order component is a function that takes an enhanced/populated version of the original component and returns it:
const HOC = Component= > EnhancedComponent;
Copy the code
First, understand why we need higher-order components:
The React documentation makes it very clear that higher-order components are a pattern for reusing component logic. The most common examples are Redux’s Connect and React-Router’s withRouter. Higher-order components were originally used to replace mixins. To sum up, there are two points:
- Logic reuse. Extract some common code logic into higher-order components, so that more components can share
- Separate concerns. The principle of “separation of logic and view” was mentioned in the previous section. Higher-order components can be used as a vehicle to implement this principle. We typically implement this by extracting the behavioral or business layer into higher-level components, leaving the presentation component to focus only on the UI
There are two main ways to implement higher-order components:
-
Props Proxy: The Proxy is passed to the Props of the wrapped component and operates on the Props. This is the most commonly used method. This can be done:
- Operating props
- Access the wrapped component instance
- To extract the state
- Wrap wrapped components with other elements
-
Inheritance Inversion: A high-level component inherits a wrapped component. Such as:
function myhoc(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { return super.render(); }}; }Copy the code
Can be achieved:
- Render hijacking: Controls the render output of wrapped components.
- Operation state: State is typically an internal detail of a component that can be exposed to subclasses by inheritance. You can add, delete, and change the state of the wrapped component, which is generally not recommended unless you know what you are doing.
In fact, advanced components can do more than the above list, advanced components are very flexible, it is all up to your imagination. You can learn about recompose, which is a library that takes the higher-order components out of bounds.
To summarize the application scenarios of higher-order components:
- Operation props: Adding or deleting props. For example, converting props, extending props, fixing props, and renaming props
- Dependency injection. Inject context or external state and logic, such as connnect for Redux, withRouter for react-Router. The old context is an experimental API, so many libraries do not keep the context, but inject it as a higher-order component
- Extend state: For example, inject state into functional components
- Avoid repeated rendering: e.g. React.memo
- Separate the logic and keep the components dumb
There is a lot of documentation available on the web for higher-order components, and this article does not attempt to describe it. Learn more about advanced components
Some specifications for higher-order components:
-
The wrapper shows the name for easy debugging
function withSubscription(WrappedComponent) { class WithSubscription extends React.Component { / *... * / } WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)}) `; return WithSubscription; } function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; } Copy the code
-
Use the React. ForwardRef to forward a ref
-
Configure ‘higher-order components’ using’ higher-order functions’ to maximize the composability of higher-order components. Redux’s Connect is a prime example
const ConnectedComment = connect( commentSelector, commentActions, )(Comment); Copy the code
You can see the benefits when composing composing with compose:
// 🙅 not recommended const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent)); // ✅ use the compose method for composition // compose(f, g, h) and (... args) => f(g(h(... Args))) is the same const enhance = compose( // These are high-order components of a single argument withRouter, connect(commentSelector), ); const EnhancedComponent = enhance(WrappedComponent); Copy the code
-
Forward all unrelated props properties to the wrapped component
render() { const{ extraProp, ... passThroughProps } =this.props; // ... return( <WrappedComponent injectedProp={injectedProp} {... passThroughProps} /> ); }Copy the code
-
Name: usually named with*, or create* if it carries parameters
2. Render Props
Render Props(Function as Child) is also a common react mode, such as the official Context API and the React-Spring animation library. The purpose of higher-order components is the same: to separate concerns and reuse the logic of the component; They are simpler to use and implement than higher-order components, and can replace higher-order components in some scenarios. The official definition is:
React is a simple technique for sharing code between React components using a prop with a function value
React does not define the type of any props, so props can also be functions. When props is a function, the parent component can pass some data to the child component via function parameters for dynamic rendering. Typical code is:
<FunctionAsChild>{(a)= ><div>Hello,World! </div>}</FunctionAsChild>
Copy the code
Example:
<Spring from={{ opacity: 0 }} to={{ opacity: 1 }}>
{props => <div style={props}>hello</div>}
</Spring>
Copy the code
In some ways, this pattern is much simpler than higher-order components, both at the implementation and usage levels. The disadvantages are also obvious:
- Poor readability, especially with multiple layers of nesting
- Poor combination. Can only be nested through JSX layer by layer, generally not more than one layer
- Suitable for dynamic rendering. Because it is confined to the JSX node, it is difficult for the current component to get the data passed by the Render props. If you want to pass it to the current component, you still have to pass it through props, the higher-order component
A Fetch component is used to make an interface request:
<Fetch method="user.getById" id={userId}>
{({ data, error, retry, loading }) => (
<Container>
{loading ? (
<Loader />
) : error ? (
<ErrorMessage error={error} retry={retry} />
) : data ? (
<Detail data={data} />
) : null}
</Container>
)}
</Fetch>
Copy the code
Before React Hooks, this pattern was used to add state to function components (or dumb components). Such as the react – powerplug
The official documentation
3. Use components to abstract business logic
In most cases, the component representation is a UI object. Components can be used not only to represent UIs, but also to abstract business objects, sometimes to solve some problems subtly.
For example, when an approver approves a request, the request initiator cannot edit it again; On the contrary, when the initiator is editing, the approver cannot approve it. This is a locking mechanism, and the backend typically maintains this’ lock ‘using a heartbeat-like mechanism, which can be either explicitly released or automatically released if it has not been activated for more than a certain amount of time, such as when the page is closed. So the front end usually uses a polling mechanism to activate locks.
General implementation:
class MyPage extends React.Component {
public componentDidMount() {
// Depending on some condition trigger, you may also listen for changes in those conditions and then stop locking polling. This logic is verbose to implement
if (someCondition) {
this.timer = setInterval(async() = > {/ / polling
tryLock();
// Error handling, can lock failure...
}, 5000); }}public componentWillUnmount() {
clearInterval(this.timer);
// The page is explicitly released when unmounted
releaseLock();
}
public componentDidUpdate() {
// Start or stop locking when the listening condition changes
// ...}}Copy the code
As MyPage becomes more and more bloated with each iteration of functionality, you start to think about abstracting this business logic. In general, it is implemented by higher-order components or hooks, but they are not flexible enough. For example, conditional locking is an awkward function to implement.
Sometimes thinking about abstracting the business as a component might solve our problem subtly, such as Locker:
/** ** Locker: FC<{onError: err => Boolean, id: string }> = props => { const {id, onError} = props useEffect(() => { let timer const poll = () => { timer = setTimeout(async () => { // ... Poll () return () => {clearTimeout(timer) releaseLock()}}, [id]) return null};Copy the code
Use the Locker
render() {
return (<div>
{someCondition && <Locker id={this.id} onError={this.handleError}></Locker>}
</div>)
}
Copy the code
There is an important point here: when we abstract a business into a component, the business logic has the same life cycle as the component. The component now only cares about its own logic, such as resource requests and releases (i.e., How), while the parent decides When and under what conditions (i.e., When), thus complying with the single responsibility principle. The above example parent controls locking dynamically with JSX conditional rendering, which is much simpler than the previous implementation
4. Hooks replace higher-order components
Personally, HOOKS are a revolutionary feature for React development, changing the way development thinks and works. The first question is, “What problem does it solve? What’s new?”
Hooks first address pain points for higher-order components or Render Props. The official ‘motive’ says:
-
- Difficult to reuse state logic between components:
-
Problem: The React framework itself does not provide a way/primitive to inject reusable logic into components. RenderProps and higher-order components are just ‘pattern-level (or language-level) stuff:
-
Previous solutions: Advanced components and Render Props. These scenarios are based on the mechanics of the component itself
- Higher-order components and Render Props create redundant node nesting. The Wrapper hell
- Having to adjust your component structure can make your code cumbersome and difficult to understand
- Higher-order components are complex and difficult to understand
- Previously, higher-order components also had to ref forwarding problems and so on
-
Hooks fix:
- Take the state logic out of the component so it can be tested and reused separately.
- Hooks can be shared between components without affecting their structure
-
- Complex components are hard to understand: Complex components are characterized by a lot of discrete state logic and side effects. For example, each lifecycle function often contains discrete logic that slowly becomes spaghetti code, but you find it hard to disassemble, let alone test, it
-
Question:
- In practice, it’s hard to break these components down into smaller ones because state is everywhere. Testing them is also difficult.
- This often leads to excessive abstraction, such as redux, which requires jumping between multiple files and requires a lot of template files and template code
-
Previous workaround: high-level components and Render Props or state managers. Slice and dice the logic and UI into smaller components
-
Hooks allow you to split a component into smaller functions based on relevant parts, such as setting up subscriptions or getting data, rather than forcing partitioning based on a lifecycle approach. You can also choose to use a Reducer to manage the local state of the component to make it more predictable
-
- Class-based components are machine – and user-unfriendly:
- Question:
- For people: need to understand this, the code is verbose
- For machines: poor optimization
- Hooks: Functional components
- New problem: You need to understand closures
New from Hooks: Hooks aim to organize a component’s internal logic into reusable smaller units that each maintain a portion of the component’s ‘state and logic’.
Image via Twitter (@sunil Pai)
-
A new way to write components. Unlike previous development approaches based on class or purely functional components, Hooks provide a cleaner API and code reuse mechanism, which makes component code shorter. For example, 👆 is a comparison of the code structure migrated to hooks, and readers can also see this presentation (90% Cleaner React).
-
More fine-grained state control (useState). Where once a component had only one setState to centrally manage component state, hooks, like components, are an aggregation unit of logic and state. This means that different hooks can maintain their own state.
-
Both hooks and components are ordinary functions.
- To the extent that components and hooks are identical (both contain state and logic). Unified functional development allows you to reduce project complexity by not switching between classes, higher-order components, or renderProps contexts. For newcomers to React, the various concepts of higher-order components, render props, and so on raised the learning curve
- Functions are the simplest unit of code reuse, and simplicity means more flexibility. Function parameter passing is more flexible than component props; Functions are also easier to combine; hooks combine other hooks or ordinary functions to implement complex logic.
- Essentially, hooks bring the concept of state to functions
-
Higher-order components can only be composed by simple nesting, whereas multiple hooks are tiled to define more complex relationships (dependencies).
-
JSX is naturally isolated by hooks, which have a clear distinction between view and logic, allowing hooks to focus more on the behavior of components.
-
Downplay the component lifecycle concept and aggregate related logic that is otherwise scattered across multiple lifecycles
-
A little bit of ‘responsive programming’, each hooks contains some state and side effects, and this data can be used to pass flow and response between hooks, see below
-
Logical reuse across platforms. This is my own imagination. After React Hooks came out, You started a vue-hooks pilot project. If things go well, are hooks possible to reuse across frameworks?
An example: an infinite load list
The basic code structure for the hooks in general is:
Function useHook(options) {// ⚛️states const [someState, setSomeState] = useState(initialValue); // ⚛️derived State const computedState = useMemo(() => computed, [dependencies]); // ⚛️refs const refSomething = useRef(); // ⚛️side effect useEffect(() => {}, []); useEffect(() => {}, [dependencies]); // ⚛️state operations const handleChange = useCallback(() => {setSomeState(newState)}, [] // ⚛️output return <div>{... }</div> }Copy the code
Custom hooks and function components have the same code structure, so sometimes hooks write more like components, and components write more like hooks. I think you can think of a component as a special hook that outputs the Virtual DOM.
Some considerations:
- Hooks can only be called at the top level of the component. Do not call hooks in loops, control flows, and nested functions
- You can only call hooks from the React function component
- Custom hooks are named use*
Summary common scenarios for hooks:
- Side effects encapsulation and listening: for example useWindowSize(listening window size), useOnlineStatus(online status)
- Side effects: useEffect, useDebounce, useThrottle, useTitle, useSetTimeout
- DOM event encapsulation: useActive, useFocus, useDraggable, useTouch
- To get the context
- Encapsulate reusable logic and state: useInput, usePromise(asynchronous request), useList(list load)
- Replace higher-order components and render Props. For example, use useRouter instead of withRouter and useSpring instead of the old Spring Render Props component
- Replacing container components
- State manager: use-global-hook, unstated
- Extended state operations: The original useState was simple, so there was a lot of room to expand, such as useSetState(emulated old setState), useToggle(Boolean toggle), useArray, useLocalStorage(synchronized persistence to local storage)
- Keep your imagination open… The search for hooks continues
Learning hooks:
- The official documentation
- React Hooks
- React Today and Tomorrow and 90% Cleaner React With Hooks
- hook-guide
- How to Build Wheels with React Hooks
- Making Sense of React Hooks
- React hooks: not magic, just arrays
5. Hooksresponsive
programming
One of Vue’s most unique features is its non-invasive responsive system, which manipulates component state in accordance with Javascript’s data manipulation conventions and then automatically responds to the page. React provides setState. For complex component states, setState can make code stinky and long. Such as:
this.setState({ pagination: { ... this.state.pagination, current: defaultPagination.current ||1,
pageSize: defaultPagination.pageSize || 15,
total: 0,}});Copy the code
Then mobx came along, and it was pretty close to the Vue development experience:
@observer
class TodoView extends React.Component {
private @observable loading: boolean;
private @observableerror? :Error;
private @observable list: Item[] = [];
// Derived state
private @computed get completed() {
return this.list.filter(i= > i.completed)
}
public componentDidMount() {
this.load();
}
public render() {
/ / /...
}
private async load() {
try {
this.error = undefined
this.loading = true
const list = await fetchList()
this.list = list
} catch (err) {
this.error = err
} finally {
this.loading = false}}}Copy the code
Mobx also has a number of drawbacks:
-
Code invasions. All components that need to respond to data changes need to use observer decorations, properties need to use Observable decorations, and data manipulation methods. The mobx coupling is deep and the cost of switching frameworks or refactoring later is high
-
Compatibility: Proxy was used for refactoring after mobx v5, but Proxy was not supported until Chrome49. You can only use V4 if you want to be compatible with older browsers, and V4 has some pits that are hard to spot for newcomers to Mobx:
- The Observable array is not really an array. Antd’s Table component, for example, does not recognize mobx’s array and needs to be passed in to components for conversion using slice
- Adding properties to an existing Observable is not automatically captured
That’s where hooks come in, which makes component state management simpler and more straightforward, and which is close to the Mobx philosophy of responsive programming:
- Declare state succinctly
State is the data that drives your application. Such as UI state or business domain state
function Demo() {
const [list, setList] = useState<Item[]>([]);
// ...
}
Copy the code
- derivative
Anything that comes from a state and doesn’t have any further interaction is derivative. This includes user views, derived states, and other side effects
function Demo(props: { id: string }) {
const { id } = props;
// Replace Mobx Observables: get lists, request when mounting or id changes
const [value, setValue, loading, error, retry] = usePromise(
async id => {
return getList(id);
},
[id],
);
// Derived state: replaces Mobx with computed
const unreads = useMemo((a)= > value.filter(i= >! i.readed), [value]);// Side effect: automatic persistence after value changes
useDebounce(
(a)= > {
saveList(id, value);
},
1000,
[value],
);
// Derived view
return <List data={value} onChange={setValue} error={error} loading={loading} retry={retry} />;
}
Copy the code
So hooks are revolutionary in that they make the flow of component state data much clearer. Instead of a class component, we might normally do data comparisons in the componentDidUpdate lifecycle methods and then trigger some methods imperative. For example, getList is triggered when an ID changes and saveList is performed when a list changes.
Hooks seem to dilute the concept of a component’s life cycle, allowing developers to focus more on relationships of states and think about component development in terms of data flow. Dan Abramov’s principle of “Don’t block the flow of data” in writing resilient components confirms my belief:
Whenever you use props and state, consider what happens if they change. In most cases, components should not handle the initial rendering and update process differently. This allows it to adapt to changes in logic.
Check out awesome-react-hooks, open source hook schemes that are interesting. For example, rxjs-hooks, a clever combination of React hooks and RXJS:
function App(props: { foo: Const value = useObservable(Inputs $=> inputs$. Pipe (map([val]) => val + 1)); [props.foo]); return <h1>{value}</h1>; }Copy the code
6. Class inheritance is also useful
As the React documentation states: “We use thousands of components in React, but we haven’t found anything that would recommend you use inheritance.” React tends to be a combination of functional programming, and object-oriented inheritance has few practical application scenarios.
Inheritance may come in handy when we need to convert some traditional third-party libraries into React component libraries. Because most of these libraries are organized using object-oriented paradigms, typically the Map SDK. Take Baidu Map as an example:
Baidu Map has various component types: Controls, Overlays, tileLayers. Each of these types has multiple subclasses. For example, overlay subclasses include Label, Marker, Polyline, etc. All subclasses have the same life cycle and are rendered to the map canvas using the addOverlay method. We can extract their lifecycle management from the parent class by inheritance, for example:
// Overlay Abstract class, which manages the Overlay lifecycle
export default abstract class Overlay<P> extends React.PureComponent<OverlayProps & P> {
protectedinitialize? :(a)= > void;
// ...
public componentDidMount() {
// Subclasses are instantiated in constructor or initialize methods
if (this.initialize) {
this.initialize();
}
if (this.instance && this.context) {
// Render to Map canvas
this.context.nativeInstance! .addOverlay(this.instance);
// Initialize parameters
this.initialProperties(); }}public componentDidUpdate(prevProps: P & OverlayProps) {
// Attribute update
this.updateProperties(prevProps);
}
public componentWillUnmount() {
// The component is uninstalled
if (this.instance && this.context) {
this.context.nativeInstance! .removeOverlay(this.instance); }}// ...
// Other common methods
privateforceReloadIfNeed(props: P, prevProps: P) { ... }}Copy the code
Subclasses make it much easier to declare their own properties/events and instantiate concrete classes:
export default class Label extends Overlay<LabelProps> {
public static defaultProps = {
enableMassClear: true};public constructor(props: LabelProps) {
super(props);
const { position, content } = this.props;
// Declare supported properties and callbacks
this.extendedProperties = PROPERTIES;
this.extendedEnableableProperties = ENABLEABLE_PROPERTIES;
this.extendedEvents = EVENTS;
// Instantiate the concrete class
this.instance = newBMap.Label(content, { position, }); }}Copy the code
The code comes from react-bdmap
Of course this is not the only solution; it can also be done using higher-order components and hooks. But for libraries that are already organized in an object-oriented paradigm, inheritance is more understandable
7. Mode box management
Modal box is used frequently in application development, especially in middle and background management system. React is not particularly fun to use. The typical code is as follows:
const Demo: FC<{}> = props => { // ... const [visible, setVisible] = useState(false); const [editing, setEditing] = useState(); const handleCancel = () => { setVisible(false); }; Const prepareEdit = async (item: item) => {// Loading detail const detail = await loadingDeatil(item.id); setEditing(detail); setVisible(true); }; const handleOk = async () => { try { const values = await form.validate(); // Save await save(editing. Id, values); / / hide setVisible (false); } catch {} }; return; <> <Table dataSource={list} columns={[{text: ' ', render: Item => {return <a onClick={() => prepareEdit(item)}> Edit </a>; }},]} / > < Modal visible = {visible} onOk = {handleOk} onCancel = {handleHide} > {/ * * form rendering /} < / Modal > < / a >; };Copy the code
The code above is too ugly, with irrelevant logic piled up under a single component that does not fit a single responsibility. So we’re going to extract the modal box-related code and put it in EditModal:
const EditModal: FC<{ id? : string; visible: boolean; onCancel: () => void; onOk: () => void }> = props => { // ... const { visible, id, onHide, onOk } = props; const detail = usePromise(async (id: string) => { return loadDetail(id); }); useEffect(() => { if (id ! = null) { detail.call(id); } }, [id]); const handleOk = () => { try { const values = await form.validate(); // Save await save(editing. Id, values); onOk(); } catch {} }; Return (<Modal Visible ={visible} onOk={onOk} onCancel={onCancel}> {detail.value && {/* Form render */}} </Modal>); }; / * * * use * / const Demo: FC < {} > = props = > {/ /... const [visible, setVisible] = useState(false); const [editing, setEditing] = useState<string | undefined>(undefined); const handleHide = () => { setVisible(false); }; const prepareEdit = async (item: Item) => { setEditing(item.id); setVisible(true); }; return; <> <Table dataSource={list} columns={[{text: ' ', render: Item => {return <a onClick={() => prepareEdit(item)}> Edit </a>; }, }, ]} /> <EditModal id={editing} visible={visible} onOk={handleHide} onCancel={handleHide}> {' '} </EditModal> </>; };Copy the code
The edit-related logic is now extracted to EditModal, but the Demo component also maintains the open state of the modal box and some data state. A complex page can have a lot of modal boxes, and the code becomes more and more disgusting, with all kinds of xxxVisible states flying around. From a practical development perspective, the simplest way to do modal box control would look like this:
Const handleEdit = item => {editmodal. show({// 🔴 starts the popover as a function call. This is consistent with the modal box idiom, which does not care about the visible state of the modal box. For example, window.confirm, wx.showmodal ().id: item.id, // 🔴 Pass data to the modal box onOk: saved => {// 🔴 Event callback refreshList(saved); }, onCancel: async () => {return confirm(' cancel '); // Control mode box hidden},}); };Copy the code
This approach is also controversial in the community. Some people think it is the anti-mode of React. Does @Lisangeng violate the React mode in modal. confirm? That’s the question. Take the picture for example:
The picture also comes from the article of Desire
The red line is time-driven (or timing driven) and the blue line is data-driven. Lu Sangeng believes that “even a React project with obvious data-driven characteristics still has many parts that are not data-driven but event-driven. Data can only drive state, only timing can drive behavior, and for a timing driven behavior, you have to harden it into a data-driven state, don’t you think it’s strange?” I’m not going to judge whether he’s right or wrong, but some scenarios are strictly ‘data-driven’ and can have a lot of template code that can be uncomfortable to write.
So how to implement?
Consider the antD Modal.confirm implementation, which uses reactdom. render for plug-in rendering, or the Context API. Close to ideal (at least in API terms) is react-comfirm:
/** * EditModal.tsx */ import { confirmable } from 'react-confirm'; const EditModal = props => { /*... * /}; export default confirmable(EditModal); /** * Demo.tsx */ import EditModal from './EditModal'; const showEditModal = createConfirmation(EditModal); const Demo: FC<{}> = props => { const prepareEdit = async (item: Item) => { showEditModal({ id: Item. id, // 🔴 Pass data to the modal box onOk: saved => {// 🔴 call refreshList(saved); }, onCancel: async someValues => {return confirm(' cancel '); // Control mode box hidden},}); }; / /... };Copy the code
The downside of using reactdom.render is that you can’t access the Context, so compromise and use the Context API as an example:
extension
- Modal.confirm
- Does modal. confirm violate the React mode?
- Use render props to abstract the state of the Modal component
- react-confirm
- How to Use React’s New Context API to easily manage modals Context-based scenarios
8. Use Context for dependency injection
Context provides a way to pass data to the component tree, eliminating the need to manually pass props properties at every level.
Context is used a lot in React applications, and the new Context API is very easy to use. Context is often used in the following scenarios:
- Share data that is considered “global” to a component tree. Such as current authenticated user, topic, I18N configuration, form status
- Configure the behavior of a component, such as ANTD’s ConfigProvider
- Communication across components. It is not recommended to communicate by ‘events’, but by’ states’
- Dependency injection
- Context can be packaged to replace Redux and Mobx state management schemes. There will be a special article on the follow-up
Context is scoped as a subtree, that is, a Context Provider can be applied to multiple subtrees, and a subtree Provider can override the value of the parent Provider. Basic structure:
import React, {useState, useContext} from 'react' export inteface MyContextValue { state: number setState: (state: number) => void } const MyContext = React.createContext<MyContextValue>( { state: // Set the default value, throw an error, must use setState with Provider: () => throw new Error(' MyContextProvider ')}) export const MyContextProvider: FC<{}> = props => { const [state, setState] = useState(1) return <MyContext.Provider value={{state, setState}}>{props.children}</MyContext.Provider> } export function useMyContext() { return useContext(MyContext) } export default MyContextProviderCopy the code
Methods in the Context default value should throw errors warning of improper use
Extension:
- Avoid repeated rendering caused by React Context
9. An immutable state
Immutable state has important implications for the React functional programming paradigm.
-
Immutable data is predictable. Immutable data makes applications easier to debug and changes to objects easier to track and derive.
For example, Redux requires that state changes can only be made through Dispatch + Reducer, and Devtool with Redux can well track how state changes are made. This feature makes a lot of sense for large applications because the state is so complex that if you don’t organize and constrain it, you don’t know where the state changed, and it’s hard to track bugs when they happen.
Therefore, immutable data is a basic requirement for state manager (Redux), which requires the entire application to be mapped by a single state. Immutable data can make the entire application predictable.
-
Immutable data also makes some complex functions easier to implement. Avoiding data changes allows us to safely retain references to old data, making it easy to undo redo, or time travel
-
Rerender judgments can be made with precision. We can simplify the shouldComponentUpdate comparison.
Popular ways to implement immutable data:
- immer
- Immutable.js
I prefer immer, there is no mental burden, according to the JS customary object manipulation way can achieve immutable data.
10. React-router: URL is state
Traditional routes are mainly used to distinguish pages. Therefore, like back-end routes (also known as static routes), front-end routes are designed to use object configuration to assign different page components to different urls. When the application is started, components matching urls are searched in the routing configuration table and rendered.
React-router V4 is a real componentized routing library. React-router is officially called dynamic routing, which refers to routes that occur during application rendering. Specifically,
At this time the MEANING of URL is not the same, URL is no longer a simple page logo, but the state of the application; Application composition is no longer limited to flat pages, but multiple areas that can be nested in response to URL state. It wasn’t loved when it first came out because of the big shift in thinking. This is more flexible, so choosing V4 doesn’t mean abandoning the old way of routing your pages.
As an example, an application consists of three areas: a sidebar holds multiple entries that load lists of the corresponding type, and clicking on a list item loads details. The three areas are cascaded
First, design a URL that expresses this cascading relationship, such as /{group}/{id}. URL design generally follows the REST style, so the application structure is roughly like this:
// App const App = () => { <div className="app"> <SideBar /> <Route path="/:group" component={ListPage} /> <Route path="/:group/:id" component={Detail} /> </div>; }; // SideBar const SideBar = () => {return (<div className=" SideBar "> {/* Use NavLink to display active status when matching */} <NavLink To ="/message"> </NavLink> <NavLink to="/task"> Task </NavLink> <NavLink to="/location"> location </NavLink> </div>); }; // ListPage const ListPage = props => { const { group } = props.match.params; / /... UseEffect (() => {load(group); }, [group]); Return <div className="list">{renderList()}</div>; }; // DetailPage const DetailPage = props => { const { group, id } = props.match.params; / /... UseEffect (() => {loadDetail(group, id); }, [group, id]); return <div className="detail">{renderDetail()}</div>; };Copy the code
extension
- The React the Router philosophy
- React Router V4
11. Component specifications
- Enable StrictMode: Enable StrictMode to detect potential problems and irregularities as soon as possible
- Third party development specification:
- Airbnb React/JSX Style Guide
- React bits
- react-in-patterns
extension
- recompose
- Write resilient components