The status quo
In react development, various modals and drawers are one of our indispensable interactions. In the case of Modal components, the following page should be familiar if you are working with a background system
This is probably the way most people would start off by creating the index.js file, which is composed of the Table + Button component.
import React, { useState } from 'react';
import { Table, Button, Divider} from 'antd';
import OpenModal from './openModal';
import ReactDOM from 'react-dom';
import './index.css';
const data = [{
name: 'Joe'.age: '12'
}, {
name: 'tom'.age: '22'
}]
const App = () = > {
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState({});
const columns = [
{
dataIndex: 'name'.title: 'name'}, {dataIndex: 'age'.title: 'age'
}, {
title: 'operation'.render: (text, record) = > {
return <div>
<a onClick={()= >ShowModal (record)} > edit</a>
<Divider type="vertical" />
<a >To view</a>
<Divider type="vertical" />
<a >delete</a>
</div>}}]const showModal = (record) = > {
setVisible(true);
if (record) {
setRecord(record)
}
};
const handleOk = () = > {
setVisible(false);
};
const handleCancel = () = > {
setVisible(false);
};
return (
<>
<Button type="primary" onClick={showModal}>Add personnel</Button>
<Table
dataSource={data}
columns={columns}
/>
<OpenModal
handleOk={handleOk}
handleCancel={handleCancel}
visible={visible}
setVisible={setVisible}
record={record}
/>
</>
);
};
ReactDOM.render(<App />.document.getElementById('root'));
Copy the code
Then create openmodal.js with the child modal component in it:
import React from 'react';
import {Modal} from 'antd';
const MemberModal = (props) = > {
return <Modal
title={'new'}visible={props.visible}
onCancel={props.handleCancel}
onOk={props.handleOk}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
};
export default MemberModal;
Copy the code
Pain points
This way of writing has several painful points:
- Many if the parent component page interaction is more complex
modal
Need to open, you may need for each onemodal
Set one for bothvisible
And the correspondingsetVisible
Function to control them separately. - Table component has edit function, open edit
modal
Box, you need to place the current row data (record
) tomodal
Inside, most of the action is in the edit function willrecord
enduresstate
Inside and then in the parent componentrender
I’m going to put this inside the functionstate
tomodal
Components.
Both of these add an extra state variable, indirectly making the code less readable; And if you have more than three modals in the same component, the readability of the code will get worse if you don’t do something about it.
To solve
For the first pain point, you can extract the repetitive logical visible variable and setVisible function; For the second pain point, if the rendering time of the floating layer component can be controlled in the edit function, then there is no need to save a record in the state.
It’s natural to think of higher-order components. Take a look at the React website for higher-order components. First is a function, followed by a function that takes a component and returns a value for the new component.
Now that you know the requirements you want, you can begin to implement them. First define a wrapper function that receives a component, second I want to render modal where the parent component triggers the open float function, so I need to return a render method. The source code is as follows:
import React from 'react';
import ReactDOM from 'react-dom';
const wrapper = (component) = > {
// Destroy the component
const destoryDialog = (element) = > {
const unmountResult = ReactDOM.unmountComponentAtNode(element);
if(unmountResult && element.parentNode) {
setTimeout(() = > {
element.parentNode.removeChild(element);
}, 300); }}// Render component
const render = ({element, component, config}) = > {
constcomInstance = React.createElement(component, { ... config,key: 'div'.closeDialog: () = > {
destoryDialog(element)
},
visible: true
})
ReactDOM.render(comInstance, element)
}
return function (config) { / / mount div
const element = document.createElement('div');
render({element, component, config});
document.getElementsByTagName('body') [0].appendChild(element); }};export default wrapper;
Copy the code
It’s also easy to use. For modal child components, you just need to introduce the wrapper function
import React from 'react';
import {Modal} from 'antd';
+ import wrapper from 'xxxx';Const MemberModal = (props) => {return <Modal title={' new '} visible={props. Visible}- onCancel={props.handleCancel}
- onOk={props.handleOk}
+ onCancel={props.closeDialog}
+ onOk={props.closeDialog}> <p>Some contents... </p> <p>Some contents... </p> <p>Some contents... </p> </Modal> };- export default MemberModal;
+ export default wrapper(MemberModal);
Copy the code
The parent component also needs to be modified a little.
import React, { useState } from 'react'; import { Table, Button, Divider} from 'antd'; import MemberModal from './openModal'; import ReactDOM from 'react-dom'; import './index.css'; Const data = [{name: 'zhang' age: '12'}, {name: "Tom, age: '22'}] const App () = = > {- const [visible, setVisible] = useState(false);
- const [record, setRecord] = useState({});
const columns = [
{
dataIndex: 'name',Title: 'name ', width: '40%'},{dataIndex: 'age',{title: 'age ', width: '40%'}, {title:' operation ', render: (text, record) => {return <div>< a onClick = {() = > showModal (' edit 'record)} > edit < / a >
- showModal(record)}><Divider type=" Divider "/> <a > view </a> <Divider type=" Divider" /> <a > delete </a> </div>}}] record = {}) => {+ MemberModal({
+ type,
+ record,
+})
- setVisible(true);
- if (record) {
- setRecord(record)
-}
};
const handleOk = () => {
setVisible(false);
};
const handleCancel = () => {
setVisible(false);
};
return (
<>
<Button
style={{float: 'right', marginBottom: '12px'}}
type="primary"
+ onClick={() => showModal('add')}
- onClick={showModal}<Table rowKey={(record) => record. Name} dataSource={data} columns={columns} />-
- handleOk={handleOk}
- handleCancel={handleCancel}
- visible={visible}
- setVisible={setVisible}
- record={record}
- />< / a >); }; ReactDOM.render(<App />, document.getElementById('root'));Copy the code
Open the add button and edit button, respectively, to print the subcomponentprops
We can get throughtype
Value of the different to determine the opening is new operation or edit operation, so as to do some logic processing, very convenient; And in the parent component, there is no need torecord
That’s stored in state.
The new problem
The idea of higher-order components solves two of the previous pain points, but creates new problems
- Floating layers will be rerendered on and off
dom
And destructiondom
Not very elegant in performance. - Because it’s dead in the code
appendChild
tobody
The floating layer cannot be hung below the current parent component - Unable to get child component’s REF
The last
The performance problems are generally perceived within the project as being within the scope of acceptance (as if there were no obvious changes). Cannot hang below the current parent component… This will have to be wrapped without a wrapper function. It is not recommended to operate the real DOM through the ref in react. So far, there is no floating layer scenario where the ref is not allowed. If there is, the wrapper function can only be used.
Leave a comment and share your tips. Learn from each other and grow together.