三:以理论结合实践方式梳理前端 React 框架 ——— 增删改查案例

生命周期

组件的生命周期指的是组件从创建到结束,包含其中组件的状态变化的这一过程所创建挂载、更新数据、销毁结束的操作

创建挂载函数:constructor、componentWillMount、componentDidMount

数据更新函数:componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、componentDidUpdate

结束销毁函数:componentWillUnmount

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body>
    <div id="main"></div>
    <script type="text/babel">
		class Header extends React.Component {
            componentWillReceiveProps() {
                console.log('componentWillReceiveProps');			// 2.1 只有上面返回 true 时,props 发生改变执行
            }
            componentWillUnmount() {
                console.log('componentWillUnmount');				// 组件销毁前执行
            }
            render() {
                return <span>{this.props.count}</span>
            }
        }
        class App extends React.Component {
            constructor() {
                super();
                this.state = { count: 0 };
            }
            componentWillMount() {
                console.log('componentWillMount');			// 1.0 页面启动执行
            }
            componentDidMount() {
                console.log('componentDidMount');			// 1.1 页面启动执行
            }
            shouldComponentUpdate() {
                return true;								// true 表示允许返回,false 表示不允许返回
            }
            componentWillUpdate() {
                console.log('componentWillUpdate');			// 2.0 只有上面返回 true 时,state 发生改变执行
            }
            componentDidUpdate() {
                console.log('componentDidUpdate');			// 2.2 只有上面返回 true 时,state 发生改变执行
            }
            handle = () => {
                this.setState({ count: this.state.count + 1 });
            }
            render() {
                const { count } = this.state;
                return (
                    <div>
                        <h1>生命周期函数: <Header count={count} /></h1>
                        <button onClick={this.handle}>点击</button>
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

todolist 实战

完成一个简单的 todolist 业务功能呈现,包含列表的增、删、改、查(提示:本地存储)


在这里插入图片描述


项目分析:将板块分成三大组件,头部处理新增、全选、批量删除业务,中部处理列表渲染、单选、修改、删除业务,底部处理分类统计,由于未涉及到数据库,使用本地存储方式进行数据的编辑操作后的保存。

(1)板块容器

板块容器是一个类组件,用于引用傀儡组件和处理业务逻辑,由于当前未使用 redux 状态管理,板块容器不能称之为一个容器组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body>
    <div id="main"></div>
    <script type="text/babel">
        class App extends React.Component {
            constructor() {
                super();
            }
            
            render() {
                return (
                    <div>
                        
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

(2)傀儡组件

上面分析得到三大组件:头部 HeaderComponent、中部 MainerComponent、底部 FooterComponent

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body>
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

分析列表数据:每列存在内容和状态,那么可以得出列表数据是一个元素为对象 { value: ‘’, state: 0 } 的数组集合,可以模拟列表数据,进行数据的传递,通过遍历将列表数据渲染出来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {
            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} />
                                    <input value={item.value} />
                                    <button>修改</button>
                                    <button>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent list={this.state.list} />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

F12 会出现一个警告提醒,大致的意思是需要绑定一个 onChange 事件,这是因为表单控件是属于受控组件,如果要多表单控件数据进行修改操作时,需要手动进行非受控操作,即:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button>修改</button>
                                    <button>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent list={this.state.list} editeItem={this.editeItem} />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

由于当前列表数据是采用声明式的,所以即使修改了数据,但刷新浏览器依然是声明式的数据,这里通过使用本地缓存的方式进行数据的读写操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            // 伪造的文本框输入提交,调用父组件文本框提交
            const fakeUpdataItem = () => props.updataItem();

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button onClick={fakeUpdataItem}>修改</button>
                                    <button>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = JSON.parse(localStorage.getItem('list')) || [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }

            updataItem = () => {
                let { list } = this.state;
                localStorage.setItem('list', JSON.stringify(list));
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent list={this.state.list} editeItem={this.editeItem} updataItem={this.updataItem} />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

通过列表索引值传值,进行列表内列的删除操作,并将最后的列表缓存本地保存以达到数据更新操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            // 伪造的文本框输入提交,调用父组件文本框提交
            const fakeUpdataItem = () => props.updataItem();

            // 伪造的复选框单选删除,调用父组件复选框单选删除
            const fakeDeleteItem = (index) => props.deleteItem(index);

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button onClick={fakeUpdataItem}>修改</button>
                                    <button onClick={() => fakeDeleteItem(index)}>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = JSON.parse(localStorage.getItem('list')) || [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }

            updataItem = () => {
                let { list } = this.state;
                localStorage.setItem('list', JSON.stringify(list));
            }

            deleteItem = index => {
                let { list } = this.state;
                list = list.filter((item, idx) => idx !== index);
                this.setState({ list });
                localStorage.setItem('list', JSON.stringify(list));
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent list={this.state.list} editeItem={this.editeItem} updataItem={this.updataItem} deleteItem={this.deleteItem} />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

列表选中需要改变自身的状态,这里通过状态取反后再取正,得到状态数值类型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {
            // 伪造的单选框选中更新,调用父组件单选框选中更新
            const fakeRadioItem = (index) => props.radioItem(index);

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            // 伪造的文本框输入提交,调用父组件文本框提交
            const fakeUpdataItem = () => props.updataItem();

            // 伪造的复选框单选删除,调用父组件复选框单选删除
            const fakeDeleteItem = (index) => props.deleteItem(index);

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} onChange={() => fakeRadioItem(index)} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button onClick={fakeUpdataItem}>修改</button>
                                    <button onClick={() => fakeDeleteItem(index)}>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = JSON.parse(localStorage.getItem('list')) || [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }
            
            radioItem = index => {
                let { list } = this.state;
                list[index].state = +!list[index].state;
                this.setState({ list });
                localStorage.setItem('list', JSON.stringify(list));
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }

            updataItem = () => {
                let { list } = this.state;
                localStorage.setItem('list', JSON.stringify(list));
            }

            deleteItem = index => {
                let { list } = this.state;
                list = list.filter((item, idx) => idx !== index);
                this.setState({ list });
                localStorage.setItem('list', JSON.stringify(list));
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent radioItem={this.radioItem} list={this.state.list} editeItem={this.editeItem} updataItem={this.updataItem} deleteItem={this.deleteItem} />
                        <FooterComponent />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

底部组件通过统计选中,未选中数即总数减掉选中数量

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            return (
                <div>
                    
                </div>
            )
        }
        function MainerComponent (props) {
            // 伪造的单选框选中更新,调用父组件单选框选中更新
            const fakeRadioItem = (index) => props.radioItem(index);

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            // 伪造的文本框输入提交,调用父组件文本框提交
            const fakeUpdataItem = () => props.updataItem();

            // 伪造的复选框单选删除,调用父组件复选框单选删除
            const fakeDeleteItem = (index) => props.deleteItem(index);

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} onChange={() => fakeRadioItem(index)} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button onClick={fakeUpdataItem}>修改</button>
                                    <button onClick={() => fakeDeleteItem(index)}>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            let { list } = props;
            let selected = list.filter(item => item.state === 1).length, unselect = list.length - selected;
            return <div>已选:{selected}未选:{unselect}</div>
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = JSON.parse(localStorage.getItem('list')) || [
                    { state: 0, value: '苹果' },
                    { state: 0, value: '橘子' },
                    { state: 0, value: '香蕉' },
                    { state: 0, value: '李子' },
                    { state: 0, value: '樱桃' },
                    { state: 0, value: '菠萝' },
                ];
                this.state = { list }		// 1. 初始化状态
            }
            
            radioItem = index => {
                let { list } = this.state;
                list[index].state = +!list[index].state;
                this.setState({ list });
                localStorage.setItem('list', JSON.stringify(list));
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }

            updataItem = () => {
                let { list } = this.state;
                localStorage.setItem('list', JSON.stringify(list));
            }

            deleteItem = index => {
                let { list } = this.state;
                list = list.filter((item, idx) => idx !== index);
                this.setState({ list });
                localStorage.setItem('list', JSON.stringify(list));
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent />
                        <MainerComponent radioItem={this.radioItem} list={this.state.list} editeItem={this.editeItem} updataItem={this.updataItem} deleteItem={this.deleteItem} />
                        <FooterComponent list={this.state.list} />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

同理,头部新增输入框也需要手动改为非受控组件进行数据编辑,由于所有业务都是在板块容器内操作的,这里需要传递两个变量到头部组件中:inputvalue 新增输入内容和 allcheck 全选状态,当然后面使用 Hooks 钩子函数的话就不需要啦。

这里需要注意的是:

  • allcheck 全选状态不一定为非选中状态,所以在初始化时,判断是否全选;
  • 删除唯一一个未选中列,allcheck 全选状态变更选中状态;
  • 全部删除列表所有列或部分列,判断 allcheck 全选状态是否全选;
  • 最后一个单选列选中的情况下,全选状态也要选中状态;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
</head>
<body style="display: flex; height: 500px; align-items: center; justify-content: center;">
    <div id="main"></div>
    <script type="text/babel">
        function HeaderComponent (props) {
            // 伪造的文本框输入更新,调用父组件文本框更新:表单控件为受控组件,需要监听 onChange 事件
            const fakeInputItem = event => props.inputItem(event);

            // 伪造的文本框输入新增,调用父组件文本框新增
            const fakeNewlyItem = () => props.newlyItem();

            // 伪造的复选框选中更新,调用父组件复选框选中更新
            const fakeCheckItem = () => props.checkItem();
            
            // 伪造的复选框全选删除,调用父组件复选框全选删除
            const fakeClearItem = () => props.clearItem();

            return (
                <div>
                    <input type="checkbox" checked={props.allcheck} onChange={fakeCheckItem} />
                    <input value={props.inputvalue} onChange={fakeInputItem} />
                    <button onClick={fakeNewlyItem}>新增</button>
                    <button onClick={fakeClearItem}>批量删除</button>
                </div>
            )
        }
        function MainerComponent (props) {
            // 伪造的单选框选中更新,调用父组件单选框选中更新
            const fakeRadioItem = (index) => props.radioItem(index);

            // 伪造的文本框输入编辑,调用父组件文本框编辑:表单控件为受控组件,需要监听 onChange 事件
            const fakeEditeItem = (event, item) => props.editeItem(event, item);

            // 伪造的文本框输入提交,调用父组件文本框提交
            const fakeUpdataItem = () => props.updataItem();

            // 伪造的复选框单选删除,调用父组件复选框单选删除
            const fakeDeleteItem = (index) => props.deleteItem(index);

            return (
                <div>
                    {
                        props.list.map((item, index) => {
                            return (
                                <div key={index}>
                                    <input type="checkbox" checked={item.state} onChange={() => fakeRadioItem(index)} />
                                    <input value={item.value} onChange={(event) => fakeEditeItem(event, index)} />
                                    <button onClick={fakeUpdataItem}>修改</button>
                                    <button onClick={() => fakeDeleteItem(index)}>删除</button>
                                </div>
                            )
                        })
                    }
                </div>
            )
        }
        function FooterComponent (props) {
            let { list } = props;
            let selected = list.filter(item => item.state === 1).length, unselect = list.length - selected;
            return <div>已选:{selected}未选:{unselect}</div>
        }
        class App extends React.Component {
            constructor() {
                super();
                let list = JSON.parse(localStorage.getItem('list')) || [];
                list.length === 0 && localStorage.setItem('list', JSON.stringify(list));
                // 如果没有数据全选状态为 false,否则根据列表状态是否存在未选的情况下为 false,只有不存在未选的情况下为 true
                let index = list.length === 0 ? 0 : list.findIndex(item => item.state === 0);
                this.state = { list, allcheck: index === -1 ? 1 : 0, inputvalue: '' }		// 1. 初始化状态
            }

            inputItem = (event) => {
                this.setState({ inputvalue: event.target.value });
            }
            
            newlyItem = () => {
                let { inputvalue, list } = this.state;
                list.push({ name: inputvalue, state: 0 });
                this.setState({ list, inputvalue: '' });
                localStorage.setItem('list', JSON.stringify(list));
            }

            checkItem = () => {
                let { allcheck, list } = this.state;
                allcheck = allcheck === 1 ? 0 : 1;
                list = list.map(item => {
                    if (item.state !== allcheck) {
                        item.state = allcheck;
                    }
                    return item;
                })
                this.setState({ allcheck, list });
                localStorage.setItem('list', JSON.stringify(list));
            }

            clearItem = () => {
                let { list } = this.state;
                list = list.filter(item => item.state !== 1);
                // 如果没有数据全选状态为 false,否则根据列表状态是否存在未选的情况下为 false,只有不存在未选的情况下为 true
                let len = list.length === 0 ? 0 : list.findIndex(item => item.state === 0);
                this.setState({ list, allcheck: len === -1 ? 1 : 0 });
                localStorage.setItem('list', JSON.stringify(list));
            }

            radioItem = index => {
                let { list } = this.state;
                list[index].state = +!list[index].state;
                let { state } = list[index];
                let idx = list.findIndex(item => item.state !== state);
                let allcheck = idx === -1 ? state : 0;
                this.setState({ list, allcheck });
                localStorage.setItem('list', JSON.stringify(list));
            }

            editeItem = (event, index) => {
                let { list } = this.state;
                list[index].value = event.target.value;
                this.setState({ list });
            }

            updataItem = () => {
                let { list } = this.state;
                localStorage.setItem('list', JSON.stringify(list));
            }

            deleteItem = index => {
                let { list } = this.state;
                list = list.filter((item, idx) => idx !== index);

                // 如果没有数据全选状态为 false,否则根据列表状态是否存在未选的情况下为 false,只有不存在未选的情况下为 true
                let len = list.length === 0 ? 0 : list.findIndex(item => item.state === 0);
                this.setState({ list, allcheck: len === -1 ? 1 : 0 });
                localStorage.setItem('list', JSON.stringify(list));
            }
            
            render() {
                return (
                    <div>
                        <HeaderComponent inputvalue={this.state.inputvalue} inputItem={this.inputItem} newlyItem={this.newlyItem} allcheck={this.state.allcheck} checkItem={this.checkItem} clearItem={this.clearItem} />
                        <MainerComponent radioItem={this.radioItem} list={this.state.list} editeItem={this.editeItem} updataItem={this.updataItem} deleteItem={this.deleteItem} />
                        <FooterComponent list={this.state.list} />
                    </div>
                );
            }
        }
        ReactDOM.render(
            <App />,
			document.getElementById('main')
		);
	</script>
</body>
</html>

技巧点

  1. 遇到一个 Huge Page,不要急着写代码,需要分析页面元素,组件分化,抽丝剥茧;
  2. 傀儡组件方法名称与容器组件执行方法名称确保一致,增强代码可读性;
  3. 逻辑业务一层一层写,计算方式要简洁,数组索引、过滤可配用,业务要全面思维;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值