React入门

安装方式

使用create-react-app脚手架可以帮我们初始化一个项目demo

npx create-react-app my-app

项目结构:public/index.html是页面模板,src/index.js是入口JS文件
在这里插入图片描述

安装完成后,进入项目文件并启动

cd react-start
npm start

运行后会弹出react图标窗口。

下面将App.js文件修改一下:

import React, { Component } from 'react';
// import './App.css';

class App extends Component {
  render() {
    return <h1>Hello, React</h1>;
  }
}
export default App;

此时react图标就会变成文字:Hello,React

基本元素介绍

什么是JSX

在上面的代码中使用了JSX,全称是JavaScript XML。继续修改App.js:

const name = 'React';
const element = (
  <div tabIndex="0" className="box">
    <h1>Hello, {name}</h1>
    <div className="name">
      <span>xiaoqi</span>
    </div>
  </div>
);
class App extends Component {
  render() {
    return element;
  }
}

element 就是一个JSX,可以看到它的特点:

  • 可以使用{}内置js表达式
  • JSX标签可以包含很多子标签
  • JSX中可以指定特定的属性(采用大驼峰),比如上面的tabIndex、className

元素渲染

元素是构成React应用的最小砖块。
创建一个元素element:

const element = (
  <div className="box">
    <h1>React 入门</h1>
    <span> 元素是构成 React 应用的最小砖块。</span>
  </div>
);

将这个渲染到DOM根结点,需要使用ReactDOM.render()方法:

ReactDOM.render(element, document.getElementById('root'));

修改index.js文件:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
// import App from './App';
// import * as serviceWorker from './serviceWorker';

const element = (
  <div className="box">
    <h1>React 入门</h1>
    <span> 元素是构成 React 应用的最小砖块。</span>
  </div>
);
ReactDOM.render(element, document.getElementById('root'));

组件

组件分为两种:Class组件和Function组件。

大多数的React应用都由很多小组件组成,将所有的小组件都添加到App主组件中,最后将App主组件渲染到入口js文件index.js中。

Class组件

创建一个Class组件只需要创建一个class类,并且继承React.Component,在render()方法中return出JSX模板。

接下里在scr文件夹中创建一个Table.js文件:

// Table组件
import React, { Component } from 'react';

class Table extends Component {
  render() {
    return (
      <table>
          <thead>
          <tr>
            <th>Name</th>
            <th>Job</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Charlie</td>
            <td>Janitor</td>
          </tr>
          <tr>
            <td>Mac</td>
            <td>Bouncer</td>
          </tr>
          <tr>
            <td>Dee</td>
            <td>Aspiring actress</td>
          </tr>
      </table>
    );
  }
}
export default Table;

将其添加到App主组件中:

class App extends Component {
  render() {
    return (
      <div className="app">
        <h1> Hello, React</h1>
        <Table />
      </div>
    );
  }
}

Function组件

上面的Table组件可以拆分为两个子组件:TableHeader和TableBody,这两个组件可以使用Function组件来创建:

// TableHeader组件
const TableHeader = () => {
  return (
    <thead>
      <tr>
        <th>Name</th>
        <th>Job</th>
      </tr>
    </thead>
  );
};

// TableBody 组件
const TableBody = () => {
  return (
    <tbody>
      <tr>
        <td>Charlie</td>
        <td>Janitor</td>
      </tr>
      <tr>
        <td>Mac</td>
        <td>Bouncer</td>
      </tr>
      <tr>
        <td>Dee</td>
        <td>Aspiring actress</td>
      </tr>
    </tbody>
  );
};

class Table extends Component {
  render() {
    return (
      <table>
        <TableHeader />
        <TableBody />
      </table>
    );
  }
}

比较一下Function组件和Class组件:

Function组件:

const functionComponent = () => {
	return  <div> Example</div>
}

Class组件:

class ClassComponent entends React.Component {
	render() {
		return <div> Example</div>
	}
}

组件通信

React使用props传递数据。

如果表格的数据很多,那么上面的TableBody组件会显得很臃肿。因此可以将tbody中要输出的数据提取出来,通过props的方式进行传递。

修改App.js文件:在App组件中生命数据charaters,表示中要输出的数据。在子组件Table上添加属性名称和数据。

class App extends Component {
  render() {
    // 提取数据
    const characters = [
      {
        name: 'Charlie',
        job: 'Janitor',
      },
      {
        name: 'Mac',
        job: 'Bouncer',
      },
      {
        name: 'Dee',
        job: 'Aspring actress',
      },
    ];
    return (
      <div className="app">
        <h1> Hello, React</h1>
        <Table characterData={characters} />
      </div>
    );
  }
}

然后在子组件Table中通过this.props.属性名来获取父组件中传递的数据:

class Table extends Component {
  render() {
    const { characterData } = this.props;
    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    );
  }
}

这里的 const { characterData } = this.props 相当于 const characterData = this.props.characterData,这里只是es6的写法。

继续将数据传递给TableBody组件。由于TableBody是一个function组件,所以其接收数据的方式跟class组件是不一样的。其使用props的方法直接是将props作为参数名传递给function组件:

// TableBody 组件
const TableBody = (props) => {
  const rows = props.characterData.map((item, index) => {
    return (
      <tr key={index}>
        <td>{item.name}</td>
        <td>{item.job}</td>
      </tr>
    );
  });
  return <tbody>{rows}</tbody>;
};

这里给每个表行都添加了一个key,事实上react创建列表时必须通过key来识别列表的每一项。

State状态

props是单向传递,并且是只读的。如果想改变数据的状态,可以使用state,与props不同的是,state可变,但是state是class组件私有的对象。
修改state不能直接修改,要使用this.setState()方法来改变。

下面实现一个功能:在每行后面添加一个删除按钮,点击之后可以删除该行。继续修改App.js:

class App extends Component {
  state = {
    characters: [
      {
        name: 'Charlie',
        job: 'Janitor',
      },
      {
        name: 'Mac',
        job: 'Bouncer',
      },
      {
        name: 'Dee',
        job: 'Aspring actress',
      },
    ],
  };
  // 箭头函数解决 class中this不绑定的问题
  removeCharacter = (i) => {
    this.setState({
      characters: this.state.characters.filter((item, index) => {
        return i !== index;
      }),
    });
  };

  render() {
    // 提取数据
    return (
      <div className="app">
        <h1> Hello, React</h1>
        <Table
          characterData={this.state.characters}
          removeCharacter={this.removeCharacter}
        />
      </div>
    );
  }
}

通过state来传递数据,并通过setState方法来改变数据。

这里将removeCharacter传递给Table组件后,还需要继续传给TableBody组件,并且还需要在TableBody组件中给每行添加一个按钮,点击之后触发此函数:

// Table组件
import React, { Component } from 'react';

// TableHeader组件
const TableHeader = () => {
  return (
    <thead>
      <tr>
        <th>Name</th>
        <th>Job</th>
      </tr>
    </thead>
  );
};
// TableBody 组件
const TableBody = (props) => {
  const rows = props.characterData.map((item, index) => {
    return (
      <tr key={index}>
        <td>{item.name}</td>
        <td>{item.job}</td>
        <td>
          <button onClick={() => props.removeCharacter(index)}>Delete</button>
        </td>
      </tr>
    );
  });
  return <tbody>{rows}</tbody>;
};

class Table extends Component {
  render() {
    const { characterData, removeCharacter } = this.props;
    return (
      <table>
        <TableHeader />
        <TableBody
          characterData={characterData}
          removeCharacter={removeCharacter}
        />
      </table>
    );
  }
}

注意:State的更新可能是异步的,React可能会把多个setState()调用合并成一个调用。

props与state的区别

  • state是管理数据,控制状态,可通过this.setState()改变
  • props是外部传入的数据参数,不可变
  • 没有state的叫无状态组件,有state的叫有状态组件
  • 多用props,少用state

事件处理

React中的事件处理和DOM元素的很相似,但在语法上有不同:

  • React事件的命名采用小驼峰(camelCase)
  • 使用JSX语法时需要传入一个函数作为事件处理函数

在上面的例子中实现了将数据存储在state状态中,并且可以从状态中删除任何数据。下面通过事件处理方式说明如何添加数据。

首先创建一个表单Form.js,将表单的初始状态设置为带有空属性的对象,并且将初始状态赋给this.state:

import React, { Component } from 'react'

class Form extends Component {
    initialState = {
        name: '',
        job: '',
    }

    state = this.initialState
    
    handleChange = (event) => {
        const { name, value } = event.target;
        this.setState({
           [name]: value,
        });
    }
    
    render() {
        const { name, job } = this.state; // 初始化时为空
        return (
            <form>
                <label htmlFor="name">name</label>
                <input
                    type="text"
                    name="name"
                    id="name"
                    value={name}
                    onChange={this.handleChange}
                />
                <label htmlFor="job">job</label>
                <input
                    type="text"
                    name="job"
                    id="job"
                    value={job}
                    onChange={this.handleChange}
                />
            </form>
        );
    }
}

export default Form;

通过onChange事件监听输入框的变化,将handleChange方法绑定到该事件上。一旦有输入,则触发执行handleChange方法,在该方法中由setState实时更新输入框的内容。

注意:在class组件中,class的方法默认不会绑定this,所以要谨慎JSX回调函数中的this。

例如上面的例子中,如果忘记绑定this,直接将this.handleChange传入onChange,调用函数时this将会是undefined。

绑定this的方式:
(1)class的方法上使用箭头函数,如上例所示,handleChange = (event) => {…} 。
(2)显式绑定:
// 直接在传入时进行绑定
onChange={this.handleChange.bind(this)}

接下来提交表单,在输入框后面增加一个提交按钮,并将onClick事件与submitForm函数进行绑定:

// 提交表单
submitForm = () => {
    this.props.handleSubmit(this.state);
    // 添加后清空state:
    this.setState(this.initialState);
}

render() {
	 ...
	 return (
		<form>
			...
		    <input type = "button" value="submit" onClick={this.submitForm} />
		</form>
	);
}

当点击提交按钮时触发subForm函数,该函数执行了从父组件app.js传递过来的handleSubmit函数:

handleSubmit = (character) => {
    // 添加数据
    this.setState({
        characters: [...this.state.characters, character]
    })
}

render() {
    return (
        <div className="app">
            <h1>Hello, React</h1>
            <Table
                characterData={this.state.characters}
                removeCharacter={this.removeCharacter}>
            </Table>
            <Form handleSubmit={this.handleSubmit} />
        </div>
    );
}

handleSubmit函数将输入的数据添加到原来的表单之中,就达到了添加数据的效果。

条件渲染

React中的条件渲染和javascript中一样,可以使用运算符if或者条件运算。

下面实现一个登陆退出按钮,根据不同的状态实现不同的效果。为了方便,还是在上面的项目基础上实现。

添加一个Login.js文件:

import React, { Component } from 'react'

// 登录
const LoginButton = (props) => {
    return <button onClick={props.click}>Login</button>;
}

// 注销
const LogoutButton = (props) => {
    return <button onClick={props.click}>Logout</button>
}

// 登录描述
const LoginGreeting = () => {
    return <span>Welcome </span>;
  };

// 注销描述
const LogoutGreeting = () => {
return <span>Please sign up</span>;
};

const Greeting = (props) => {
    const {isLoggedIn} = props;
    return <div>{isLoggedIn ? <LoginGreeting/> : <LogoutGreeting/>}</div>
}


class LoginControl extends Component {
    // 初始状态
    state = {
        isLoggedIn: false,
    };

    handleLoginClick = () => {
        this.setState({
            isLoggedIn: true,
        });
    };

    handleLogoutClick = () => {
        this.setState({
            isLoggedIn: false,
        });
    };

    render() {
        const isLoggedIn = this.state.isLoggedIn;
        let button;
        // 通过if语句来进行条件渲染
        if (isLoggedIn) {
            button = <LogoutButton click={this.handleLogoutClick}></LogoutButton>
        } else {
            button = <LoginButton click={this.handleLoginClick}></LoginButton>
        }

        return (
            <div>
                <Greeting isLoggedIn = {isLoggedIn} />
                {button}
            </div>
        );
    }
}

export default LoginControl

然后修改app.js,在表单后添加登录按钮和greeting:

<LoginControl />

就可以实现按照是否登录条件来显示登录按钮状态了:
在这里插入图片描述

列表和key

在上面的例子中TableBody组件中使用了trtd元素,可以看到,每个tr中都有一个特殊的属性key,这个key是必须的。

key帮助React识别哪些元素发生了改变,比如删除或添加。key就可以作为这个改变的元素的唯一标识,因此key在列表中应该是独一无二的。通常使用id来作为元素的key,如果元素没有确定的id时,可以使用元素索引index作为key

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值