官方文档:https://zh-hans.reactjs.org/docs/hello-world.html
核心概念
一、元素渲染
1、ReactDom.render(template,targetDOM):将模板转换为html并插入到指定DOM根节点
//index.js入口文件
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render( //ReactDom.render(template,targetDOM)方法将模板转换为html并插入到指定DOM节点
<h1>hello world</h1>
document.getElementById('root')
);
2、元素不可变,一旦创建就无法更改其子元素和属性,如同电影的单帧,代表某一特定时刻的UI,更新UI的唯一方式是创建一个新的元素并传入ReactDOM.render()
function fan() {
const element = <div><h1>hello world</h1><h1>{new Date().getSeconds()}</h1></div>;
ReactDOM.render(element,document.getElementById('ele'));
}
setInterval(fan, 1000);
return (
<div className="App">
<div id="ele" />
</div>
);
3、只更新需要更新的部分
二、JSX
1、在jsx中嵌入表达式:使用花括号,花括号内放置任何js有效表达式:
formate(user),a+b,user.url
function fan(a) {
return ('hello'+a)
}
const name = 'world';
const element=<div>Hello,{name}</div>;
const ele = <div>fan(name)</div>;
2、可以存在if语句,for循环中,作为函参传入,函数返回值
function fan(a) {
if(a){return (<h1>hello+{a}</h1>)}
return (<h1>hello</h1>)
}
3、指定属性:字符串使用引号,表达式使用花括号
const user={url:'www.baidu.com',data:'0'};
const element = <div tabIndex="0">111</div>; //注意:jsx语法中的属性要以小驼峰(camelCase)命名
const img = <img src={user.url} />;
4、jsx防止注入攻击
在reactDOM渲染前内容都被转换成了字符串,确保在你的应用中,永远不会注入那些并非自己明确编写的内容。
5、babel将jsx转换成react.createElement()的调用函数,创建了一个对象,下面三种写法等价
const element = <div className='ele'>hello</div>;
相当于:
const element=react.createElement(
'h1',
{className:'ele'},
"hello"
);
相当于:
const element={
type:'div',
props:{
className:'ele',
children:'hello'
}
};
三、组件和props
1、概念:
组件: 将UI拆分为独立、可复用代码片段,类似js函数,入参props,返回描述页面的React元素。组件名称大写字母开头
props:当react元素为用户自定义组件时,jsx会将接收的属性和子组件(children)转换为单个对象传给组件,这个对象就是props,不可被修改
2、函数组件与class组件
函数组件
function Fan(props) { //自定义组件
return <h1>hello,{props.name}</h1>;
}
const element = <Fan name="fan" />; //name会作为props的属性传递给Fan自定义组件
ReactDOM.render(element,document.getElementById('root'))
ES6 class组件:
class Fan extends React.Component{
render(){
return <h1>hello,{this.props.name}</h1>
}
}
3、组合组件与提取组件
要求:将下面的组件提取出UserInfo组件,使得Comment为组合组件
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
提取组件:
function Avatar(props){
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
}
function UserInfo(props) {
return (
<div>
<Avatar user={props.user} /> //二级嵌套子组件
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
)
}
组合组件:
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author}/> //一级嵌套子组件
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
四、state与生命周期
1、概念:
state:组件内部私有
2、class组件中才可以使用state与生命周期函数,将函数组件转换成class组件步骤:
创建es6 class,继承于React.Component
添加空render(){}方法
将函数体移动到render()方法中
在render()方法中使用this.props替换props
3、封装一个组件,设置自己的计时器并每秒钟更新UI上当前秒
import React,{Component} from 'react';
class App extends Component{ //定义了一个名称为App的类,它继承于React.Component
constructor(props){//创建一个构造函数,为this.state赋初值
super(props); //class组件使用props调用父类的构造函数
this.state={ //唯一可以直接为state赋值的地方
date:new Date(),
timerId:''
}
}
componentDidMount(){ //生命周期函数:组件已经挂载到根节点上触发
this.timerId = setInterval(()=>this.tick(),1000)//初始化定时器,每一秒调用一次tick函数
}
componentWillUnmount(){
clearInterval(this.timerId)//生命周期函数:组件将要销毁:清除定时器
}
tick(){
this.setState({date:new Date()}) //
}
render(){
return(
<div className="App">
<div>{this.state.date.getSeconds()}</div>
</div>
)
}
}
export default App;
总结调用顺序:
1、ReactDOM.render()会使用react调用Clock组件中的构造函数初始化this.state,显示this.state中的初始值
2、调用Clock组件的render()方法,根据Clock渲染的输出来更新DOM
3、当Clock的输出插入到DOM中后,调用ComponentDidMount()生命周期函数,向浏览器请求设置一个定时器来每秒调用一次tick()方法
4、浏览器每秒调用一次tick方法,该方法调用this.setState()进行一次UI更新,React知道state改变了就会重新调用render(),更新DOM
5、Clock组件被移除前调用ComponentWillUnmount()生命周期函数清除定时器
4、state的使用
(a)除了构造函数中,其他地方都不能直接修改state,不会重新渲染组件,要使用this.setState({aa:" "})
, setState调用后会将其提供的对象合并到当前的state
(b) setState的更新可能是异步的:性能方面的考虑,react会合并多个setState再调用
影响:不要依赖this.prop和this.state来更新下一个状态,例如
this.setState({aa:props.a+state.b})
解决方法:传给setState一个函数,函参1为上一个state,函参2为props
this.setState((state,props)=>{aa:props.a+state.b})
普通函数也可以
©向下的单向数据流
本组件可以将state作为props向下传递给子组件
<Formate date={this.state.date}> //在父组件中调用子组件,将state作为数据流传递下去
function Formate(props){ //自定义子组件
return <div>{this.props.date}</div>
}
五、事件处理
1、命名:小驼峰(camelCase)
2、引入形式:jsx需要传入一个函数作为事件处理函数而不是字符串
传统:<button onclick="handleClick()">点击</button>
jsx: <button onClick={handleClick} >点击</button>
3、阻止默认行为:react中只能使用e.preventDefault,而不能使用return false
4、es6 class组件中将事件处理函数声明为class类的方法
js通用:如果你没有在方法后面添加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this。
绑定this方法如下
(a)constuctor中bind
constructor(props) {
super(props);
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() { }
render() {
return (
<button onClick={this.handleClick}>aa </button>
);
}
}
(b)publish class fields语法(建议使用)
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
©onclick中使用箭头函数或者绑定bind
class LoggingButton extends React.Component {
handleClick() { }
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>aa </button>
);
}
}
5、事件函数传参
//e为函参2,bind方式会将事件对象e隐式传递下去
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
六、条件渲染
1、三种方式
(a)、if else
(b)、&&运算符
true && element //&&符号前面是true,返回后面的元素
false &&element//&&符号前面是false,返回false
<div>
{messages.length > 0 && <h2> hello</h2>}
</div>
©、三目运算
condition? true:false
2、阻止组件渲染
render(){
if(!props.warn){
return null;
}
return(...)
}
七、列表与key
1、使用.map()渲染列表,实例如下:
render(){
const number = this.props.number;
const listItem = number.map((item)=>{
//注意:每一项要有唯一标识赋值给key
return <li key={item.id}>{item}</li>
});
return(
<div className="App">
<ul>{listItem}</ul>
</div>
) }}
使用jsx语法创建一个含有map循环的变量:
render() {
const number = this.props.number;
const listItem = <ul>
{number.map((item) => {
return <li key={item.id}>{item}</li>
})}
</ul>;
return (
<div className="App">
{listItem}
</div>
)
}
2、key
作用:react用其来标识哪个元素发生了变化(添加或者删除),在兄弟节点之间必须是唯一的标识。一般使用数据中的id来作为元素的key。key将信息传递给react,而不会传递给组件
提问:是否可以使用索引作为key值
如果使用索引,那么当数据发生变化,索引对应的新值和索引对应的旧值对于react来说认为是没有变化的,会降低性能甚至引发组件状态问题。
如果数据不发生变化可以使用index
key的使用位置
map()方法中的元素需要设置key属性
八、表单
1、input,select,textarea都是受控表单,使用value属性绑定state中的值,通过onChange函数setState修改state的值。
class App extends Component {
constructor(props) {
super(props);
this.state = {
input:"",
textarea:"",
select:["3","2"]
}
}
handleInput=(e)=>{
this.setState({
input:e.target.value
})
};
handleSelect=(e)=>{
this.setState({
select:e.target.value
})
};
handleTextarea=(e)=>{
this.setState({
textarea:e.target.value
})
};
render() {
return (
<div className="App">
<input type="text" value={this.state.input} onChange={this.handleInput}/>
<textarea value={this.state.textarea} onChange={this.handleTextarea}/>
<select value={this.state.select} onChange={this.handleSelect} multiple>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
文件input标签:从用户存储设备上选择一个或多个文件,非受控组件
<input type="file"/>
受控输入空值: 一般情况下绑定了value值但是没有绑定onchange函数则会阻止用户更改input中值,除非为value设置了null或者undefined
<input type="text" value={null} />
</div>
)
}
}
export default App;
2、同时处理多个类型的input表单
添加name属性,根据e.target.name选择执行的操作
handleChange=(e)=>{
const value=e.target.name==='aa'?e.target.checked:e.target.value;
const name=e.target.name
this.setState({[name]:value}) //es6的计算属性名称:给属性动态的修改值
}
...
return(
<div>
<input name="aa" checked={this.state.aa} type="checkbox"/>
<input name="bb" value={this.state.bb} type="number"/>
</div>
)
九、状态提升
应用场景:多个子组件互动(比如更改子组件1中的值会影响子组件2,更改子组件2会影响子组件1)
使用:将影响的子组件中的state中的值提升到他们共有的父组件中的props,然后处理
示例:
共有父组件
import React, {Component} from 'react';
import Temperature from '../src/components/temperature/Tempreture';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
scale:'c',
temperature:"",
}
}
handleC=(e)=>{
this.setState({scale: 'c', temperature:e.target.value});
};
handleF=(e)=>{
this.setState({scale: 'f', temperature:e.target.value});
};
render() {
function tryConvert(temperature, convert) { //温度转换函数
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
function toC(f) { //转换成摄氏度
return (f - 32) * 5 / 9;
}
function toF(c) { //转换成华氏度
return (c * 9 / 5) + 32;
}
const c = this.state.scale === 'c' ? this.state.temperature : tryConvert(this.state.temperature, toC);
const f = this.state.scale === 'f' ? this.state.temperature: tryConvert(this.state.temperature,toF);
return (
<div>
提升了温度这个子组件中的state数据提升到了父组件中,在父组件中进行转换交互后使用props自上而下的数据流传递下去,从而实现两个无关的子组件实现互动
<Temperature
scale="c"
temperature={c}
onTemperatureChange={this.handleC} />
<Temperature
scale="f"
temperature={f}
onTemperatureChange={this.handleF} />
</div>
);
}
}
export default App;
Temperature 子组件
class Temperature extends Component{
constructor(props){
super(props);
this.state={};
}
render(){
return(
<div>
<fieldset>
<legend>请输入{this.props.scale}温度</legend>
<input type="text" value={this.props.temperature} onChange={this.props.onTemperatureChange}/>
</fieldset>
</div>
)
}
}
export default Temperature
十、组合和继承 ==类似于vue的插槽slot的概念
组合:
组件之类的react元素本质是对象,可以把他们当作props中的数据传递下去。props可以是基本数据类型、函数、react元素
1、使用this.props.children
父组件在引用子组件A时,在子组件标签内组合一个子组件B,在子组件内可以使用this.props.children获取子组件2
父组件
return (
<div>
<A>
<B />
</A>
</div>
);
子组件A
render(){
return(
<div>
{this.props.children} //此处渲染的是B组件
</div>
)
}
2、指定任意的props
父组件
return (
<div>
left属性为指定的props
<A left={<B />} />
</div>
);
子组件A
render(){
return(
<div>
{this.props.left} //此处渲染的是B组件
</div>
)
}
十一、react哲学:拿到一个UI图和API后的思考步骤
1、根据UI分析组件之间的嵌套关系
2、根据props数据按照自上而下(嵌套简单)或者自下而上(嵌套复杂)开发无交互的静态页面
3、分析最小state集合:根据事件推移发生变化;不是从父组件流下来的数据;不能根据其他的state或者props数据计算得到。根据state数据放置在合适组件(使用该state的最高阶组件)并添加交互
4、添加反向数据流:使用回调函数反向传递
高级知识点
一、无障碍
1、保证html语义:解决ol,ul,dl等在div包裹下失效问题
Fragment、短语法<></>实现语义HTML多个元素的添加且不在DOM中增加额外节点
<dl>
{props.items.map(item => (
Fragments 也需要添加key
<Fragment key={item.id}>
<dt>{item.term}</dt>
</Fragment>
))}
</dl>
或者:
<dl>
{props.items.map(item => (
当fragmen没有需要添加的属性时,可以使用<></>
由于循环数组需要添加key属性,所以不能使用短语法
<>
<dt>{item.term}</dt>
</>
))}
</dl>
2、表单控制
lable for属性规定 label 与哪个表单元素绑定。在jsx中写成htmlFor
作用: 在点击label时会自动将焦点移动到绑定的元素上
实现功能:点击lable,input框获得焦点
<label htmlFor="namedInput">Name:</label>
<input id="namedInput" type="text" name="name"/>
实现功能:点击男,id=nan的checkbox选中
<label for="nan">男</label>
<input type="checkbox" id="nan" />
<label for="nv">女</label>
<input type="checkbox" id="nv" />
二、代码分割
1、打包概念:将文件引入合并到一个文件中,形成一个bundle,在页面上引入一次性加载。
代码分割:就是按照需求的先后顺序分割代码生成多个bundle,按需加载,避免bundle过大造成加载时间过长
2、基于路由的代码分割,路由懒加载(按需加载
)
() => import(’@/components/HelloWorld’)
vue/react框架都可以使用,因为是由webpack提供的功能
三、全局数据的传递
方式一:使用props一直传递(适用三层组件内)
方式二:context
应用场景:一些属性需要层层传递,全局数据的传递
作用:可以避免层层传递,将值植入组件树,任何组件都可以取到
使用:
1、创建生产者和消费者
export const {Provider, Consumer} = React.createContext(defaultValue);
注意:组件所处的树中没有匹配到 Provider 时, defaultValue才会生效,与provider中的value值无关
2、在父组件中添加生产者,需要传递的数据放置在value中
<Provider value={‘传递下去的数据’}>
<Temperature/>
</Provider>
注意:provider的value发生变化,消费组件重新渲染,不受shouldComponentUpdate控制。当祖先组件退出更新情况下也可以更新。
3、在子孙组件中引入消费者,注意consumer里面的必须是函数
import {Consumer} from "../../父组件";
<Consumer>
{(context) =>
<div> {context} </div>
}
</Consumer>
示例:
父组件
import React, {Component} from 'react';mport Temperature from "./components/temperature/Tempreture";
export const {Provider, Consumer} = React.createContext();
class App extends Component { //定义了一个名称为App的类,它继承于React.Component
constructor(props) {//创建一个构造函数
super(props); //class组件使用props调用父类的构造函数
this.state = {
list: [1, 1, 2, 2, 3, 4, 5, 6, 8]
}
}
render() {
return (
<Provider value={this.state.list}>
<Temperature/>
</Provider>
)
}
}
export default App;
子组件
import React, {Component} from 'react'
import {Consumer} from "../../App";
class Temperature extends Component {
constructor(props) { super(props); }
render() {
return (
<Consumer>
{(context) => <div> {context} </div>}
</Consumer>
)
}
}
export default Temperature
方式三:在父组件将配置好属性的孙组件作为属性传递下去
function Page(props) {
const user = props.user;
为孙组件Feed配置user属性
const content = <Feed user={user} />;
const topBar = (
<NavigationBar>
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
</NavigationBar>
);
return (
<PageLayout
topBar={topBar}
content={content}
/>
);
}
四、高阶组件
五、refs转发
一、组件结构与数据绑定
1、super关键字:
参考:http://www.phonegap100.com/thread-4911-1-1.html
Es6中super关键字,用于类的继承,指代父级整个prototype或者__proto__指向的对象(实例),返回父类的this对象,然后对其进行加工。所以子类必须在constructor方法中调用super方法,因为子类没有自己的this对象。
3、React绑定数据、对象 、属性的知识点
a. 所有的模板要被一个根节点包含起来 (xml语法)
b.模板元素不要加引号
c.{}数据渲染
d.绑定属性:绑定属性的两种方法:1.e.target.name 2.this.refs.name
ref的使用:1.给元素设置ref属性2.通过this.refs.refName获取dom节点
注意:
class要变成 className; for 要变成 htmlFor ; style属性如下:
<div> style={{'color':'blue'}}>{this.state.title}</div>
<div style={{'color':this.state.color}}>{this.state.title}</div>
e.循环数据要加key(加在最外层标签中即可) 如下:
.map((value,key)=>{return(<div key={key}> </div>)})
f.构造函数中一定要注意 super
g.组件名称首字母大写、组件类名称首字母大写
react的MVVM流图:
二、 事件处理函数
1、概念
以类继承方式定义的组件中,为能方便调用当前组件的其他成员方法或属性,需将事件处理函数运行时的 this 指向当前组件实例。
2、绑定事件处理函数this的几种方法:
3、 事件对象e/event
4、键盘事件onKeyUp、onKeyDown
5、生命周期函数
三、表单处理
react类似vue的双向数据绑定:监听函数(如:onchange)监听表单value值的改变,在监听函数内部修改state的值(使用this.setState({}))改变model更新state中input的Value的值;点击按钮获取新的state中input 的Value值(表单元素中设置value=this.state.inputValue)。
注意:判断等于的问题,由于value为字符串,所以不能使用绝对等于===,使用==
四、父子组件及传值
解决html 标签构建应用的不足。
使用组件的好处:把公共功能单独抽离成一个组件,哪里使用哪里引入。
1、父子组件:组件的相互调用中,我们把调用者称为父组件,被调用者称为子组件
父组件给子组件传值
1.在调用子组件的时候定义一个属性<Header msg='首页'></Header>
2.子组件里面this.props.msg
说明:父组件不仅可以给子组件传值,还可以给子组件传方法,以及把整个父组件传给子组件。
父组件主动获取子组件的数据
1、调用子组件的时候指定ref的值<Header ref='header'></Header>
2、通过this.refs.header
获取整个子组件实例
2、父子组件传值,如果父组件调用子组件的时候不给子组件传值,可以在子组件中使用defaultProps定义的默认值
验证父组件传值的类型合法性:propTypes,方法如下:
import PropTypes from 'prop-types';
类.propTypes={name:PropTypes.string};
使用实例:
<Headerf title={this.state.headers}/> //父组件只给出了title属性的值,没有num、str属性的值
//子组件
import React,{Component} from "react";
import PropsType from 'prop-types';
class Headerf extends Component{
constructor(props) {
super(props);
}
render() {
return(
<div>
{this.props.title}//父组件给子组件传值
<h3>{this.props.num}</h3>
<h3>{this.props.str}</h3>
</div>
)
}
}
Headerf.defaultProps={// 给子组件的属性赋值,注意所在位置
num:520,
str:'111'
};
Headerf.propsType={
str:PropsType.string
};
export default Headerf;
五、以上知识点运用的实例
import React, {Component} from "react"; //引入react脚手架
import Logo from "../assets/images/logo.svg"; //引入图片
class FormProcess extends Component{
constructor(props){
super(props);
this.state={
msg:"我是原始数据",
list:[111,222,333,444],
name:'请输入用户名',
sex:1,
city:'',
cities:['北京','上海','天津'],
hobby:[{'title':'睡觉','checked':true},{'title':'吃饭','checked':false},{'title':'打游戏','checked':true}],
info:''
};
}
setData=(str)=>{ //注意函数名称以及组件名称不能与内置函数名称相同
this.setState({ // this.setState方法更改数据
msg: str,
list:[555,666,777,888],
inputMessage:"请输入",
username:''
});
};
getState=()=>{
alert(this.state.msg);
};
inputChange=(event)=>{ //表单处理函数:使用事件对象绑定属性值
this.setState({
inputMessage:event.target.value
})
};
getValue=()=>{
alert(this.state.inputMessage);
};
getRefValue=()=>{ //表单处理函数:使用ref绑定属性值:1.给元素设置ref属性 2.通过this.refs.refName获取dom节点*/
let val=this.refs.username.value;
this.setState({
username:val})
};
getRef=()=>{
alert(this.state.username);
};
inputKeyUp=(e)=>{ // 键盘处理函数,实现功能:按下回车后弹出input的输入值 回车keyCode为13*/
if(e.keyCode === 13){
alert(e.target.value);
}
};
handleName=(e)=>{ // 约束性与非约束性组件
this.setState({
name:e.target.value
})
};
//==============================以下是各种表单的处理==========================
handleSubmit=(e)=>{
e.preventDefault(); //阻止submit的提交事件*/
console.log(this.state.name,this.state.sex,this.state.city,this.state.hobby,this.state.info);
};
handleSex=(e)=>{
this.setState({
sex:e.target.value
});
};
handleSelect=(e)=>{
this.setState({
city:e.target.value
})
};
handleCheckbox=(key)=>{
var hobby=this.state.hobby;
hobby[key].checked=! hobby[key].checked;
this.setState({
hobby:hobby
})
};
handleTextarea=(e)=>{
this.setState({
info:e.target.value
})
};
render(){
var listValue=this.state.list.map( (item,key)=>{
return(
<li key={key}>{item}</li>
)
});
return(
<div style={{opacity:this.state.opacity}}> //数据渲染
<h3>1.数据渲染:</h3>
<ul>{listValue}</ul>
<h3>2.图片引入的两种方法:本地、远程:</h3> //图片引入
<img src="https://www.baidu.com/img/bd_logo1.png" alt="图片无法显示" style={{ width:"100px"}}/>
<img src={Logo} alt="图片无法显示" style={{ width:"100px"}}/>
<h3>3.react实现类似vue的双向数据绑定:</h3> //双向数据绑定
<button onClick={this.getState}>state</button>
<button onClick={this.setData.bind(this,"修改后")}>点击改变state</button>
<h3>4.事件对象:</h3>
<button abd="123" onClick={this.getEvent}>获取事件节点</button>
<h3>5.绑定属性的两种方法:</h3>
<input type="text" onChange={this.inputChange} placeholder="事件对象方法获取属性值"/>
<button onClick={this.getValue}>获取input框的值</button>
<br/>
<input ref="username" type="text" placeholder="ref方式获取属性值" onChange={this.getRefValue}/>
<button onClick={this.getRef}>ref方法获取属性值</button>
<h3>6.键盘事件:</h3>
<input type="text" onKeyUp={this.inputKeyUp} placeholder="按下回车显示value值"/>
<h3>7.约束性与非约束性组件</h3>
<form onSubmit={this.handleSubmit}>
用户名:<input type="text" value={this.state.name} onChange={this.handleName}/>
<br/>
性别:
<input type="radio" value="1" checked={this.state.sex==1} onChange={this.handleSex}/>男
<input type="radio" value="2" checked={this.state.sex==2} onChange={this.handleSex}/>女
<br/>
居住城市:
<select value={this.state.city} onChange={this.handleSelect}>
{
this.state.cities.map((value,key)=>{
return(
<option key={key}>{value}</option>
)
})}
</select>
<br/>
爱好:{
this.state.hobby.map((item,key)=>{
return(
<span key={key}>
<input type="checkbox" checked={item.checked} value={item.title} onChange={this.handleCheckbox.bind(this,key)}/> {item.title}
</span>
)
})
}
<br/>
备注:<textarea value={this.state.info} onChange={this.handleTextarea}/>
<br/>
<input type="submit" defaultValue="提交"/>
</form>
</div>
)
}
}
export default FormProcess;
六、ToDolist实例
import React, {Component} from "react";
import "../assets/css/todolist.css";
import {Button} from "antd";
class ToDolist extends Component {
constructor(props){
super(props);
this.state={
list:[]
}
}
addData=(e)=>{ //增加todo事件函数
if(e.keyCode===13){
var tempList=this.state.list;
tempList.push({
'title': e.target.value,
'checked':false
});
this.setState({
list:tempList
});
e.target.value='';/*表单置为空*/
localStorage.setItem('todolist',JSON.stringify(tempList)); /*设置缓存数据*/
}
};
handleCheckbox=(key)=>{ //操作todo事件函数
var tempList=this.state.list;
tempList[key].checked=!tempList[key].checked;
this.setState({
list:tempList
});
localStorage.setItem('todolist',JSON.stringify(tempList)); /*设置缓存数据*/
};
removeData=(key)=>{ //移除todo事件函数
var tempList=this.state.list;
tempList.splice(key,1);
this.setState({
list:tempList
});
localStorage.setItem('todolist',JSON.stringify(tempList));/*通过setItem与getItem获得的是字符串格式的数据,通过json更改一下,设置缓存数据:application==localstorage里面查看*/
};
componentDidMount() { /*生命周期函数:加载完成后触发,实现缓存数据*/
var todolist=JSON.parse(localStorage.getItem('todolist'));
if(todolist){
this.setState({
list:todolist
})
}
}
render(){
return(
<div>
<header className="head">ToDolist: <input type="text" onKeyUp={this.addData}/></header>
<h3>待办事项</h3>
<hr/>
<ul>
{
this.state.list.map((item,key)=>{
if(!item.checked){
return(
<li key={key}>
<input type="checkbox" checked={item.checked} onChange={this.handleCheckbox.bind(this,key)}/>
{item.title}
<Button type="primary" onClick={this.removeData.bind(this,key)}>删除</Button>
</li>
)
}
})
}
</ul>
<h3>已完成事项</h3>
<hr/>
<ul>
{
this.state.list.map((item,key)=>{
if(item.checked){
return(
<li key={key}>
<input type="checkbox" checked={item.checked} onChange={this.handleCheckbox.bind(this,key)}/>
{item.title}
<button onClick={this.removeData.bind(this,key)}>删除</button>
</li>
)
}
})
}
</ul>
</div>
)
}
}
export default ToDolist;
七、路由
react-router可以让跟组件动态的挂载不同的其他组件,根据用户访问的地址动态的加载不同组件
参考链接:https://reacttraining.com/reac-router/web/example/basic
安装:npm install react-router-dom --save
1、路由配置
import {BrowserRouter as Router, Link} from "react-router-dom";
import Root from "../component/Root";
<Router>
<Link to="/user">用户中心</Link>
<Route exact path='/user' component={Root}/>
</Router>
2、页面跳转传值的几种方式
a. 动态路由传值(动态路由概念:path后面可以传入不同值)
路由配置: 路由配置加 :参, <Route path="/content/:aid" component={Content}>
跳转: Link to 使用了es6的模板字符串<Link to={'/content/${value.aid}'}>{value.title}</Link>
获取:componentDidMount函数中获取this.props.match.params
b. get传值:路由普通配置
跳转:Link to中加上?参=值
获取:componentDidMount函数中获取this.props.location.search。使用了node.js的url模块来解析this.props.location.search里面的地址(react官网==dom element(https://reactjs.org/docs/dom-elements.html))
3、js实现跳转路由
八、react请求
1、axios请求
2、fetchJsonp请求
查看接口是否符合jsonp请求,将接口放在浏览器地址栏中后面加上&callback=xxxx,返回的数据如果是xxxx({response…})格式,说明该接口支持jsonp请求
九、知识点案例–无人收银系统
主页面
import React, {Component} from 'react';
import axios from 'axios';
import '../assets/css/orderSystem.css';
import {Link} from "react-router-dom";
class OrderSystem extends Component{
constructor(props){
super(props);
this.state={
domain:"http://a.itying.com/",
list:[]
}
}
getData=()=>{
var api= `${this.state.domain}api/productlist`;
axios.get(api)
.then((response)=>{ //在内部使用到this要用箭头函数
this.setState({
list:response.data.result
});
}).catch(function(error){
console.log(error);
});
};
componentDidMount(){
this.getData(); //加载时请求数据铺页面
};
render(){
return(
<div className="order-system">
<br/>
<button style={{width:"150px"}}><Link to={"/login"}>js实现跳转之登陆功能</Link></button> //使用js跳转到Login模块
<br/>
{
this.state.list.map((value,key)=>{
return(
<div key={key}>
<h3 className="title">{value.title}</h3>
<ul className="ul-list">
{value.list.map((v,k)=>{ //知识点1:嵌套循环铺数据
return(
<li key={k} className="list">
<Link to={`/detail/${v._id}`}>
<div>
<img style={{width:"100px",height:"100px"}} src={`${this.state.domain}${v.img_url}`}/>
<p>{v.title}</p>
<p className="price">¥{v.price}</p>
</div>
</Link>
</li>
) })
</ul>
</div>
) }) }
</div>
) }}
export default OrderSystem;
//Detail详情页
import React, {Component} from 'react';
import axios from 'axios';
import '../assets/css/orderSystem.css';
import {Link} from "react-router-dom";
class Detail extends Component {
constructor(props){
super(props);
this.state={
domain:"http://a.itying.com/",
list:[]
}
}
getData=(pid)=>{
var api= `${this.state.domain}api/productcontent?id=${pid}`;
axios.get(api)
.then((response)=>{
this.setState({
list:response.data.result[0]
});
}).catch(function(error){
console.log(error);
});
};
componentDidMount() {
let pid=this.props.match.params._id; //获取动态路由传值
this.getData(pid);
};
render(){
return(
<div className="detail">
<Link to={`/order`} className="go-back">
返回
</Link>
<div className="content">
{this.state.list.img_url?<img src={`${this.state.domain}${this.state.list.img_url}`}/>:""}{/*知识点3:执行顺序导致的报错*/}
<p>{this.state.list.title}</p>
<p dangerouslySetInnerHTML={{__html: this.state.list.content}}/>{/*知识点2:react中关于html的解析*/}
</div>
</div>
)
}
}
export default Detail;
//登陆页
import React, {Component} from 'react';
import {Redirect} from 'react-router-dom';
class Login extends Component {
constructor(props){
super(props);
this.state={
loginFlag:false
}
}
handleLogin=(e)=>{
e.preventDefault();//注意:阻止默认的提交行为
const username=this.refs.username.value;
const password=this.refs.password.value;
if(username==="admin" && password==="111111"){
this.setState({
loginFlag:true
})
}else{
alert("用户名或密码错误!");
}
};
render(){
if(this.state.loginFlag){
return <Redirect to={"/order"} />
}
return(
<form onSubmit={this.handleLogin}>
<input type="text" ref="username" placeholder="用户名"/>
<br/>
<input type="password" ref="password" placeholder="密码"/> <br/> <br/>
<input type="submit" value="执行登陆" />
</form>
)
}
}
export default Login;
十、路由的模块化
路由配置文件:
import EmbedRoute from "../component/EmbedRoute"; //一级路由
import User from "../component/User"; //二级路由
import Main from "../component/user/Main"; //三级路由
import Info from "../component/user/Info";
import Shop from "../component/Shop";
import ShopList from "../component/shop/ShopList";
import ShopAdd from "../component/shop/ShopAdd";
import HomePage from "../component/HomePage";
/*路由的模块化*/
let routes=[
{
path: "/embedroute",
component: EmbedRoute,
routes:[{ 二级路由的配置
path: "/embedroute",
component: HomePage,
},{
path: "/user",
component: User,
routes:[{
path: "/user",
component: "Main"
},{
path: "/user/info",
component: "Info"
}]
},{
path: "/shop",
component: Shop,
routes:[
{
path: "/shop",
component: "ShopList"
},{
path: "/shop/shopadd",
component: "ShopAdd"
}
]
}]
}];
export default routes;
//组件中的应用
import React,{Component} from 'react';
import { BrowserRouter as Router,Route,Link } from 'react-router-dom';
class EmbedRoute extends Component{
constructor(props) {
super(props);
}
componentDidMount() {
const list=[];
this.props.routes.map((route,key)=>{
return(
<div key={key}>
if(route.routes){
list.push(route.routes)
}
</div>
)});};
render(){
return(
<Router>
<div className="embedroute">
<header className="head">
<Link to="/embedroute" className="link">首页</Link>
<Link to="/user" className="link">用户</Link>
<Link to="/shop" className="link">商品</Link>
</header>
</div>
{
this.props.routes.map((route,key)=>{ // this.props.routes包含着每一级的路由信息,具体看下面的图片
return(
<Route key={key} path={route.path} component={route.component}/>
)}
)
}
</Router>
)
}
}
export default EmbedRoute;
一级路由信息:
二级路由信息:
十一、AntDesign