React实现点击按钮,表格添加一行的效果

    这几天基于react写了一个小demo测试,主要实现的功能是:在输入框中输入文字,点击添加按钮,在下方的表格中会自动添加一行数据,点击删除按钮后,该行数据被删除。

    先来看看最后的效果图:

    操作前

    操作后

  

    可能样式没怎么调,看着有点别扭,表格样式扒的是菜鸟教程上的,觉得还行的也可以用这个样式哦,或者改成自己喜欢的即可,最重要的是功能实现就好。

    1.开发环境搭建

    这个在这里我们就不细说了,npm及node.js的安装是前提哦!

    国内使用 npm 速度很慢,你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org

    create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

    create-react-app 自动创建的项目是基于 Webpack + ES6 。

    执行以下命令创建项目:

$ cnpm install -g create-react-app
$ create-react-app my-app
$ cd my-app/
$ npm start

    其中my-app就是你自己的项目名字,如果已经创建好项目,按着提示启动项目即可。若是之前就已经创建好了项目,直接进入项目的目录,输入命令:npm start启动项目就OK啦!

    在浏览器中打开http://localhost:3000/就是我们项目运行的地址。

    创建好的项目目录结构如下:

      

    2.修改src/App.js文件

    manifest.json 指定了开始页面 index.html,一切的开始都从这里开始,所以这个是代码执行的源头。

    我最后的文件目录如下:

    

    在index.js里面,我们可以将ReactDOM.render做一点改变,如下:

ReactDOM.render(
    <App />,
    document.getElementById('container')
);

    下面的那个id就是我们自己在index.html里定义的div的id,那么主要问题来了,在App.js中,这个逻辑要怎么理清呢?

    首先,对于主体结构,我们应该是没有什么疑问的,无非就是input输入框和按钮。要完成我们的需求,需要两个方法,分别是添加和删除。

    其次,对于表格中的数据,我们可以通过一个数组去储存它。点击添加按钮时,就向数组中添加一个元素;点击删除按钮时,将该元素从数组中删除。

    至此,思路应该还是相对清晰了,那我们来一起看看代码吧:

let i=0;

class App extends React.Component {
    constructor(props) {
        super(props);
        this.add=this.add.bind(this);
        this.delete=this.delete.bind(this);
        this.state={
            lists:[]
        }
    }

    add(){
        const lists=this.state.lists;
        const info = document.getElementById("add").value;
        lists.push({"id": ++i,"value":info});
        this.setState({lists:lists})
    }

    delete(e){
        const index=e.target.getAttribute("data-index");
        const lists=this.state.lists;
        document.getElementById(index).remove();
        this.setState({lists:lists})

    }

    render() {
        return (
            <div>
                <input type="text" id="add"/>
                <button onClick={this.add}>添加</button>
                <table id="customers">
                    <tbody>
                    <tr>
                        <th>内容</th>
                        <th>操作</th>
                    </tr>
                    {this.state.lists.map((data)=>{
                        console.log(data);
                        return <List key={data.id} index={data.id} info={data.value} delete={this.delete}/>
                    })}
                    </tbody>
                </table>
             </div>
        )
    }
}

export default App;

    add()方法中,info是为了获取我们输入的内容,再将其添加到lists数组中,而这里添加id属性,则是为了删除方便,直接获取元素的下标,通过获取其下标移除元素。

    3.自定义组件List

    它主要传达的就是我每一行的元素,直接上代码:

class List extends React.Component {
    render() {
        return (
            <tr id={this.props.index} className="alt">
                <td>
                    <input type="text" defaultValue={this.props.info} />
                </td>
                <td>
                    <button onClick={this.props.delete} data-index={this.props.index}>删除</button>
                </td>
            </tr>
        )
    }
}

    4.表格样式

    最后要解决的就是样式问题啦,当然,大家还是自定义就好了。这个样式可能是有点奇怪,不过觉得看的过去的伙伴还是可以将就一下滴!

    嗯嗯,这个样式放在css里,要用的时候直接导进去就好了。

#customers
{
  font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
  width:30%;
  border-collapse:collapse;
}
#customers td, #customers th
{
  font-size:1em;
  border:1px solid #98bf21;
  padding:3px 7px 2px 7px;
}
#customers th
{
  font-size:1.1em;
  text-align:left;
  padding-top:5px;
  padding-bottom:4px;
  background-color:#A7C942;
  color:#ffffff;
}
#customers tr.alt td
{
  color:#000000;
  background-color:#EAF2D3;
}

  到了这里,表面上我们的需求已经得到实现,但事实上,这里面还存在着许多漏洞。细心的小伙伴们会发现,在我们删除元素的时候只是单纯的把元素移除掉了,也就是说,只是页面上看不到了,但实际的元素仍然存在数组中。所以,我们实际上需要删除的是数组里面的元素,通过splice可删除数组中的元素。

  接下来,我们将通过Ant Design组件实现点击按钮,增加一行数据的需求,先来看看最后的效果图:

  操作前:

  

  操作后:

  

  emm,这个效果看起来确实要比之前的舒服,在项目开始之前,我们需要部署好我们的环境,这个时候除了之前下载的依赖,还需要使用npm或yarn安装:

$ npm install antd --save

  或

$ yarn add antd

这里忘了说一句,这里也需要redux的环境部署,所以还需要 npm install redux命令。  

下一步需要做什么呢?先看看我们的目录结构,方便梳理我们要做的是什么:

  这里我们的文件命名和方法其实也涉及到了redux,但这并不影响我们的理解,简单来说,actions表示的是动作指令,定义事件的state,reducers里定义的函数通过action触发,component中定义的是组件。当然了,他们之间的交互关系,建议大家可以去看看官方文档:

  

  A:目录清楚后,首先看入口文件index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import TodoList from './component/TodoList';

ReactDOM.render(
    <TodoList />,
    document.getElementById('root')
);

  我想,这里就不用多解释了,render一个自定义组件TodoList,然后将其在页面中渲染出来。

  B:组件TodoList中我们主要是定义了四个方法,分别是

(1)handleInputChange:获取输入类型和input的值
(2)handleStoreChange:获取最新的数据
(3)handleBtnClick:点击按钮后添加数据
(4)handleItemDelete:删除数据

  具体代码如下:

class TodoList extends Component{
    constructor(props){
        super(props);
        // 获取store里的所有state数据
        this.state = store.getState();
        store.subscribe(this.handleStoreChange)
    }

    handleInputChange = (e) => {
        // 获取输入类型和input的值
        // const action = {
        //     type: 'CHANGE_INPUT_VALUE',
        //     value: e.target.value
        // }
        const action = getInputChangeAction(e.target.value);
        // 把action传给store
        store.dispatch(action);
        // store自动传给reducer
    }

    // reducer返回newState之后,store传递给newState给组件
    handleStoreChange = () => {
        // 获取最新的数据
        this.setState(store.getState());
    }

    handleBtnClick = () => {
        // const action = {
        //     type: 'ADD_TODO_ITEM'
        // };
        const action = getAddItemAction();
        store.dispatch(action);
    }

    handleItemDelete = (index) => {
        // const action = {
        //     type: 'DELETE_TODO_ITEM',
        //     index: index
        // };
        const action = getDeleteItemAction(index);
        store.dispatch(action);
    }

    render(){
        return (
            <div style={{margin:'10px',marginLeft:'10px'}}>
                <div>
                    <Input
                        value={this.state.inputValue}
                        placehoder="todo list "
                        style={{width:'300px'}}
                        onChange={this.handleInputChange}
                    />
                    <Button
                        type= "primary"
                        onClick={this.handleBtnClick}
                    >提交</Button>
                </div>
                <List
                    style={{marginTop:'10px',width:'300px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={(item,index) => (
                        <List.Item>
                            <span style={{position:'relative'}}>{item}</span>
                            <Button style={{position:'absolute',right:'8px',top:'8px'}} onClick={this.handleItemDelete} >删除</Button>
                        </List.Item>
                        )
                    }
                />

            </div>
        )
    }
}

export default TodoList;

  C:其中,getInputChangeAction、getAddItemAction和getDeleteItemAction三个方法,来源于我们的actionCreator.js中的自定义函数:

export const getAddItemAction = () => ({
    type: ADD_TODO_ITEM
});

export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value: value
});

export const getDeleteItemAction = (index) => ({
    type: DELETE_TODO_ITEM,
    index: index
});

  D:定义的type是来自我们的actionTypes.js,其实,我们也可以把它合并在一个js里,具体怎么写,还是看个人习惯啦。

export const ADD_TODO_ITEM = 'add_todo_item';
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const DELETE_TODO_ITEM = 'delete_todo_item';

  E:最后要说的是在TodoList中我们用到的store是来源于reducers文件夹下的index.js封装的store。

const store = createStore(
    reducer
);

export default store;

  F:reducers文件夹下的reducer设置的是一个默认状态

import {CHANGE_INPUT_VALUE,ADD_TODO_ITEM,DELETE_TODO_ITEM} from '../actions/actionTypes';

const defaultState = {
    inputValue: '',
    list: []
};

export default (state=defaultState,action)=>{
    //input
    if (action.type===CHANGE_INPUT_VALUE){
        const  newState=JSON.parse(JSON.stringify(state));
        //简单的深拷贝
        newState.inputValue=action.value;
        return newState;
    }

    //button
    if (action.type===ADD_TODO_ITEM){
        //把老数据拷贝一份
        const  newState=JSON.parse(JSON.stringify(state));
        //在列表中新加输入框内容
        newState.list.push(newState.inputValue);
        //点击提交之后,输入框清空
        newState.inputValue='';
        // console.log(newState);
        //返回给store
        return newState;
    }

    //点击删除
    if (action.type===DELETE_TODO_ITEM){
        const newState=JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index,1);
        return newState;

    }
    return state;
}

 

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值