1.React
React 构建用户界面的JavaScript库,主要用于构建UI界面。
特点:
- 声明式的设计
- 高效,采用虚拟DOM来实现DOM的渲染,最大限制的减少DOM的操作。
- 灵活,跟其他库灵活搭配使用
- JSX,俗称JS里面写HTML,JavaScript语法的扩展
- 组件化、模块化。代码容易复用,2016年之前大型项目非常喜欢react
- 单向数据流。没有实现数据的双向绑定。数据 =》视图=》事件=》数据
创建项目
- 通过script引入使用,仅用于学习调试使用
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
- 通过react的脚手架,创建项目进行开发,部署。
- 安装脚手架 Create React App
npm install -g create-react-app
- 创建项目
create-react-app 项目名称
2. React元素渲染
JSX元素对象,或者组件对象,必须只有1个根元素(根节点)
React JSX
优点:
- JSX执行更快,编译为JavaScript代码时进行优化
- 类型更安全,编译过程如果出错就不能编译,及时发现错误
- JSX编写模板更加简单快捷(不要给VUE比)
注意:
- JSX必须要有根节点
- 正常的普通HTML元素要小写,如果时大写,默认为是组件
3.JSX表达式
html的样式类名要写className 因为class在js中是关键字
- 由HTML元素构成
- 中间如果需要插入变量用{}
- {}中间可以使用表达式
- {}中间表达式中可以使用JSX对象
- 属性和html内容一样都是用{}来插入内容
4.JSX_style样式
const exampleStyle={background:"skyblue",borderBottom:"1px solid red"};
const element =(<div><h1 style={exampleStyle}>hello</h1></div>)
const element =(<div><h1 style={'abc'+classStr}>hello</h1></div>)
let classStr2 = ['abc2','redBg2'].join(" ");
let element2 = (<div><h1 className={classStr2}>helloworld</h1></div>)
- class中,不可以存在多个class属性
<div class=‘abc’ class={‘acitive’}>错误演示 - style样式中,如果存在多个单词的属性组合,第二个单词开始,首字母大写(也可以双引号引起来)
- 多个类共存的操作
- 注释 必须在{}里面写 {/* 这里是注释 */}
5.React组件
- 函数式组件
function Childcom(props){
let title = <h2>我是副标题</h2>
return(
<div>
<h1>函数式组件</h1>
{title}
</div>
)
}
- 类组件
class HelloWorld extends React.Component{
console.log(this.props);
render(){
return(
<div>
<h1>类组件</h1>
</div>
)
}
}
函数式组件与类组件的区别和使用,函数式比较简单,一般用于静态没有交互事件
内容的组件页面。类组件,一般又称为动态组件,那么一般会有交互或者数据修改的操作。
- 函数式组件
- 类组件
- 复合组件:组件中又有其他的组件,复合组件中既可以有类组件又可以有函数组件。
6.React State 相当于vue的data,但是使用方式不同
class Clock extends React.Component{
constructor(props){
super(props);
// 状态(数据)--》view
// 构造函数初始化数据,将需要改变的数据初始化到state中
this.state = {
time:new Date().toLocaleTimeString()
}
// console.log(this.state);
}
render(){
// console.log("这是渲染函数");
// this.state.time = new Date().toLocaleTimeString();
return(
<div>
<h1>当前时间:{this.state.time}</h1>
</div>
)
}
// 生命周期函数 组件渲染完成时的函数
componentDidMount(){
setInterval(()=>{
// console.log(this.state.time);
// 切勿直接修改state数据,直接state重新渲染内容,需要使用setState
// this.state.time = new Date().toLocaleTimeString(); 错误的方式
// 通过this.setState修改完数据后,并不会立即修改DOM里面的内容,react会在
// 这个函数内容所有设置状态改变后,统一对比虚拟DOM对象,然后再统一修改,提升性能
// 小程序也是借鉴REACT状态管理操作
this.setState({
time:new Date().toLocaleTimeString()
})
},1000)
}
}
ReactDOM.render(
<Clock/>,
document.getElementById('root')
)
7.React父传子数据传递
-
props 父传递给子组件数据,单向流动,不能子传递给父 props的传递,可以是任意的类型。
-
props可以设置默认值
HelloMessage.defaultProps = {name:“老陈”,msg:“helloworld”}
注意: props可以传递函数,props可以传递父元素的函数,就可以去修改
父元素的state,从而达到传递数据给父元素。
// 在父元素中使用state去控制子元素props的从而达到父元素数据传递给子元素
class ParentCom extends React.Component{
constructor(props){
super(props);
this.state = {
isActive: true
}
this.changeShow = this.changeShow.bind(this);
}
changeShow(){
this.setState({
isActive: !this.state.isActive
})
}
render(){
return(
<div>
<button onClick={this.changeShow}>控制子元素显示</button>
<ChildCom isActive={this.state.isActive} />
</div>
)
}
}
class ChildCom extends React.Component{
constructor(props){
super(props)
}
render(){
let strClass = null;
if(this.props.isActive){
strClass = 'active'
}else{
strClass = ''
}
return(
<div className={"content"+ strClass}>
<h1>我是子元素</h1>
</div>
)
}
}
ReactDOM.render(
<ParentCom />,
document.getElementById("root")
)
8.react数据传递子传父
调用父元素的函数从而操作父元素的数据,从而实现数据从子元素传递至父元素
// 子传父
class ParentCom extends React.Component{
constructor(props){
super(props);
this.state = {
childData: null
}
}
render(){
return(
<div>
<h1>子元素传递给父元素的数据:{this.state.childData}</h1>
<ChildCom setChildData = {this.setChildData}/>
</div>
)
}
setChildData=(data)=>{
this.setState({
childData: data
})
}
}
class ChildCom extends React.Component{
constructor(props){
super(props);
this.state = {
msg:"Hello world"
}
}
render(){
return(
<div>
<button onClick={this.sendData}>传递helloworld给父元素</button>
<button onClick={()=>{this.props.setChildData('直接调用props的函数')}}>传递helloworld给父元素</button>
</div>
)
}
sendData=()=>{
// console.log(this.state.msg);
console.log(this.props.setChildData);
// 将子元素传递给到父元素,实际就说调用父元素传递进来的父元素函数
this.props.setChildData(this.state.msg);
}
}
ReactDOM.render(
<ParentCom/>,
document.getElementById('root')
)
9.React事件
特点:
- react事件,绑定事件的命名,驼峰命名法。
- {},传入一个函数,而不是字符串
事件对象:React返回的事件对象是代理的原生的事件对象,如果想要查看事件对象的具体值,必须直接输出事件对象的属性。
注意:
- 原生,阻止默认行为时,可以直接返回return false
- 在react中,阻止默认必须使用e.preventDefault()
react事件传参
class ParentCom extends React.Component{
constructor(props){
super(props);
}
parentEvent = (e)=>{
console.log(e);
e.preventDefault();
}
parentEvent1 = (msg,e)=>{
console.log(msg);
console.log(e);
}
render(){
return(
<div>
<form action='http://www.baidu.com'>
<div className='child'>
<h1>hello world</h1>
<button onClick={this.parentEvent}>提交</button>
</div>
</form>
{/* <button onClick={this.parentEvent1('134')}>提交1</button> */}
{/*使用es6箭头函数传递多个参数*/}
<button onClick={(e)=>{this.parentEvent1('fdsfsd',e)}}>提交1</button>
{/*不使用es6箭头函数传递多个参数的方式*/}
<button onClick={function(e){this.parentEvent1('dsfasf',e)}.bind(this)}>提交1</button>
</div>
)
}
}
ReactDOM.render(
<ParentCom/>,
document.getElementById('root')
)
10.React条件渲染
React中条件渲染即和Javascript中,条件运算,如if…else… 三元运算符等。
- 直接通过条件运算返回要渲染的JSX对象
function UserGreet(props){
return (<h1>欢迎登录</h1>)
}
function UserLogin(props){
return(<h1>请先登录</h1>)
}
class ParentCom extends React.Component{
constructor(props){
super(props);
this.state = {
isLogin:false
}
}
render(){
if(this.state.isLogin){
return (<UserGreet/>)
}else{
return (<UserLogin/>)
}
}
}
ReactDOM.render(
<ParentCom/>,
document.getElementById('root')
)
- 通过条件运算符得出JSX对象,再将JSX对象渲染到模板中
function UserGreet(props){
return (<h1>欢迎登录</h1>)
}
function UserLogin(props){
return(<h1>请先登录</h1>)
}
class ParentCom extends React.Component{
constructor(props){
super(props);
this.state = {
isLogin:false
}
}
render(){
let element = null;
if(this.state.isLogin){
element = <UserGreet/>
}else{
element = <UserLogin/>
}
return(
<div>
<h1>这是头部</h1>
{element}
<h1>这是三元运算符的操作</h1>
{this.state.isLogin ? <UserGreet/>:<UserLogin/>}
<h1>这是尾部</h1>
</div>
)
}
}
ReactDOM.render(
<ParentCom/>,
document.getElementById('root')
)
11.React列表渲染
将列表内容拼装成数组放置到模板当中。将数据拼装成数组的JSX对象
class Welcome extends React.Component{
constructor(props){
super(props);
this.state = {
list:[
{
title:'第一节 React事件',
content:"事件内容"
},
{
title:'第二节 React数据传递',
content:"数据传递内容"
},
{
title:'第三节 条件渲染',
content:"条件渲染内容"
}
]
}
}
render(){
let listArr = [];
for(let i=0;i<this.state.list.length;i++){
let item = (
<li>
<h3>{this.state.list[i].title}</h3>
<p>{this.state.list[i].content}</p>
</li>
)
listArr.push(item);
}
return(
<div>
<h1>今天课程内容</h1>
<ul>
{listArr}
</ul>
</div>
)
}
}
ReactDOM.render(
<Welcome/>,
document.getElementById('root')
)
改进
使用数组的map方法,对每一项数据按照JSX的形式进行加工,最终得到1个每一项都是JSX对象的数组,再将数组渲染到模板中。
key值需要放置到每一项中
function ListItem(props){
return(
<li>
<h3>{props.index+1}:{props.data.title}</h3>
<p>{props.data.content}</p>
</li>
)
}
class ListItem2 extends React.Component{
constructor(props){
super(props)
}
clickEvent = (index,title,event)=>{
alert(index+'-'+title);
}
render(){
return(
<li onClick={(e)=>{this.clickEvent(this.props.index,this.props.data.title,e)}}>
<h3>{this.props.index+1}:{this.props.data.title}</h3>
<p>{this.props.data.content}</p>
</li>
)
}
}
class Welcome extends React.Component{
constructor(props){
super(props);
this.state = {
list:[
{
title:'第一节 React事件',
content:"事件内容"
},
{
title:'第二节 React数据传递',
content:"数据传递内容"
},
{
title:'第三节 条件渲染',
content:"条件渲染内容"
}
]
}
}
clickFn = (index,title,event)=>{
alert(index+'-'+title);
}
render(){
let listArr = this.state.list.map((item,index)=>{
return(
// <ListItem key={index} data={item} index={index}/>
<ListItem2 key={index} data={item} index={index}/>
)
})
return(
<div>
<h1>今天课程内容</h1>
<ul>
{listArr}
</ul>
<h1>复杂没有用组件完成列表</h1>
{
this.state.list.map((item,index)=>{
return(
<ul>
<li key={index} onClick={(e)=>{this.clickFn(index,item.title,e)}}>
<h3>{index+1}:{item.title}</h3>
<p>{item.content}</p>
</li>
</ul>
)
})
}
</div>
)
}
}
ReactDOM.render(
<Welcome/>,
document.getElementById('root')
)
)
11.生命周期
生命周期即是组件从实例化到渲染到最终从页面中销毁,整个过程就说生命周期,在这个生命周期中,我们有许多可以调用的事件,也俗称为钩子函数
生命周期的3个状态:
- Mounting:将组件插入到DOM中
- updating:将数据更新到DOM中
- unmounting:将组件移出DOM中
生命周期中的钩子函数(方法、事件)
- componentWillMount:组件将要渲染
- componentDidMount:组件渲染完毕
- componentWillReceiveProps:组件将要接受props数据,查看接收props的数据
- shouldComponentUpdate:组件接收到新的state或者props,判断是否更新。返回布尔值
- componentWillUpdate:组件将要更新
- componentDidUpdate:组件已经更新
- componentWillUnmount:组件将要销毁
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
class ComLife extends Component{
constructor(props){
super(props);
this.state = {
msg:'hello world'
}
console.log('constructor构造函数')
}
componentWillMount(){
console.log('componentWillMount组件将要渲染');
}
componentDidMount(){
console.log('componentDidMount组件渲染');
}
componentWillReceiveProps(){
console.log('componentWillReceiveProps组件将要接收新的state和props');
}
shouldComponentUpdate(){
// 如果希望更新,就返回为真,否则为false
console.log('进行判断是否要更新内容');
if(this.state.msg =='hello world'){
return true;
}else{
return false;
}
}
componentWillUpdate(){
console.log('componentWillUpdate组件将要更新');
}
componentDidUpdate(){
console.log('componentDidUpdate组件更新');
}
componentWillUnmount(){
console.log('componentWillUnmount移除')
}
changeMsg = ()=>{
this.setState({
msg:'hello'
})
}
render(){
console.log('render渲染函数')
return(
<div>
<h1>{this.state.msg}</h1>
<button onClick={this.changeMsg}>组件更新</button>
</div>
)
}
}
class ParentCom extends Component{
constructor(props){
super(props);
this.state= {
isShow:true
}
}
removeCom = ()=>{
this.setState({
isShow:false
})
}
render(){
if(this.state.isShow){
return (
<div>
<button onClick={this.removeCom}>移除</button>
<ComLife />
</div>
)
}else{
return <h1>将comlife已移除</h1>
}
}
}
ReactDOM.render(
<ParentCom />,
document.getElementById('root')
)
12.React插槽
组件中写入内容,这些内容可以被识别和控制。React需要自己开发支持插槽功能。
原理:
组件中写入的HTML,可以传入到props中。
组件中根据HTML内容的不同,插入的位置不同
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
class ChildCom extends Component{
render(){
let headerCom,mainCom,footerCom;
this.props.children.forEach((item,index)=>{
console.log(item.props);
if(item.props['data-position'] === 'header'){
headerCom = item;
}else if(item.props['data-position'] === 'main'){
mainCom = item;
}else{
footerCom = item;
}
})
return(
<div>
<div className='header'>{headerCom}</div>
<div className='main'>{mainCom}</div>
<div className='footer'>{footerCom}</div>
</div>
)
}
}
class ParentCom extends Component{
render(){
console.log(this.props);
return(
<div className='App'>
<h1>组件插槽</h1>
{this.props.children}
<ChildCom>
<h1 data-position="header">这是放置到头部的内容</h1>
<h1 data-position="main">这是放置到主要的内容</h1>
<h1 data-position="footer">这是放置到尾部的内容</h1>
</ChildCom>
</div>
)
}
}
class RootCom extends Component{
constructor(props){
super(props);
this.state = {
arr:[1,2,3]
}
}
render(){
return(
<ParentCom>
<h2 data-name='a' data-index={this.state.arr[0]}>子组件1</h2>
<h2 data-name='b' data-index={this.state.arr[1]}>子组件2</h2>
<h2 data-name='c' data-index={this.state.arr[2]}>子组件3</h2>
</ParentCom>
)
}
}
ReactDOM.render(
<RootCom />,
document.getElementById('root')
)
13.React路由
根据不同的路径,显示不同的组件(内容);React使用的库react-router-dom
安装
cnpm install react-router-dom --save
ReactRouter三大组件:
- Router:所有路由组件的根组件(底层组件),包裹路由规则的最外层容器.
属性:basename=>设置此路有根路径,router可以在1个组件中写多个 - Route:路由规则匹配组件,显示当前规则对应的组件
- Link: 路由跳转的组件
注意:如果要精确匹配,那么可以在route上设置exact属性。
Router使用案例
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
// hash模式 哈希模式的变更不会触发请求
// import {HashRouter as Router,Link,Route} from 'react-router-dom';
// history模式 哈希模式的变更不会触发请求,需后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom';
function Home(){
return(
<div>
<h1>admin首页</h1>
</div>
)
}
function Me(){
return(
<div>
<h1>admin个人中心</h1>
</div>
)
}
function Product(){
return(
<div>
<h1>admin产品页面</h1>
</div>
)
}
class App extends Component{
render(){
return(
<div id='app'>
<div>所有页面普通内容</div>
<Router>
<Route path='/' exact component={()=>(<div>首页</div>)}></Route>
<Route path='/me' component={()=>(<div>me</div>)}></Route>
<Route path='/product' component={()=>(<div>product</div>)}></Route>
</Router>
<Router basename='/admin'>
<div className='nav'>
{/* <Link to='/'>Home</Link>
<Link to='/product'>Product</Link>
<Link to='/me'>个人中心</Link> */}
</div>
<Route path='/' exact component={Home}></Route>
<Route path='/product' component={Product}></Route>
<Route path='/me' exact component={Me}></Route>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
Link组件可以设置to属性来进行页面的跳转,to属性可以直接写路径的字符串,也可以通过1个对象,进行路径的设置,例如
Link的replace属性:点击链接后,将新地址替换成历史访问记录的原地址
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
// hash模式 哈希模式的变更不会触发请求
// import {HashRouter as Router,Link,Route} from 'react-router-dom';
// history模式 哈希模式的变更不会触发请求,需后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom';
function Home(){
return(
<div>
<h1>admin首页</h1>
</div>
)
}
function Me(props){
console.log(props);
return(
<div>
<h1>admin个人中心</h1>
</div>
)
}
function Product(){
return(
<div>
<h1>admin产品页面</h1>
</div>
)
}
class App extends Component{
render(){
let meObj = {
pathname:'/me', //跳转的路径
search:"?username=admin", //get请求参数
hash:"#abc", //设置的HASH值
state:{ //传入组件的数据
msg:'hello world'
}
}
return(
<div id='app'>
<Router>
<div className='nav'>
<Link to='/'>Home</Link>
<Link to='/product'>Product</Link>
<Link to={meObj} replace>个人中心</Link>
</div>
<Route path='/' exact component={Home}></Route>
<Route path='/product' component={Product}></Route>
<Route path='/me' exact component={Me}></Route>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
动态路由实现
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
// hash模式 哈希模式的变更不会触发请求
// import {HashRouter as Router,Link,Route} from 'react-router-dom';
// history模式 哈希模式的变更不会触发请求,需后端匹配使用
import {BrowserRouter as Router,Link,Route} from 'react-router-dom';
function Home(){
return(
<div>
<h1>admin首页</h1>
</div>
)
}
function Me(props){
console.log(props);
return(
<div>
<h1>admin个人中心</h1>
</div>
)
}
function Product(){
return(
<div>
<h1>admin产品页面</h1>
</div>
)
}
function News(props){
console.log(props);
return(
<div>
<h1>新闻页,新闻id:{props.match.params.id}</h1>
</div>
)
}
class App extends Component{
render(){
let meObj = {
pathname:'/me', //跳转的路径
search:"?username=admin", //get请求参数
hash:"#abc", //设置的HASH值
state:{ //传入组件的数据
msg:'hello world'
}
}
return(
<div id='app'>
<Router>
<div className='nav'>
<Link to='/'>Home</Link>
<Link to='/product'>Product</Link>
<Link to={meObj} replace>个人中心</Link>
<Link to="/news/456789">新闻页面</Link>
</div>
<Route path='/' exact component={Home}></Route>
<Route path='/product' component={Product}></Route>
<Route path='/me' exact component={Me}></Route>
<Route path='/news/:id' component={News}></Route>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
重定向组件
如果访问某个组件时,如果有重定向组件,那么就会修改页面路径,使得页面内容显示为所定向路径的内容
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router,Route,Link,Redirect} from 'react-router-dom';
function LoginInfo(props){
if(props.location.state.loginState === 'success'){
return <Redirect to='/admin'></Redirect>
}else{
return <Redirect to='/login'></Redirect>
}
}
let FormCom = ()=>{
let pathObj = {
pathname:'/logininfo',
state:{
loginState:'success'
}
}
return(
<div>
<h1>表单验证</h1>
<Link to={pathObj}>登录验证后页面</Link>
</div>
)
}
class App extends Component{
render(){
return(
<div>
<Router>
<Route path='/' exact component={()=>(<h1>首页</h1>)}></Route>
<Route path='/form' component={FormCom}></Route>
<Route path='/admin' component={()=>(<h1>admin页</h1>)}></Route>
<Route path='/login' component={()=>(<h1>登录页</h1>)}></Route>
<Route path='/logininfo' component={LoginInfo}></Route>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
Switch组件
多个相同path,可匹配多次
让switch组件内容的route只匹配1个,只要匹配到了,剩余的路由规则将不再匹配
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router,Route,Link,Redirect,Switch} from 'react-router-dom';
function LoginInfo(props){
if(props.location.state.loginState === 'success'){
return <Redirect to='/admin'></Redirect>
}else{
return <Redirect to='/login'></Redirect>
}
}
let FormCom = ()=>{
let pathObj = {
pathname:'/logininfo',
state:{
loginState:'success'
}
}
return(
<div>
<h1>表单验证</h1>
<Link to={pathObj}>登录验证后页面</Link>
</div>
)
}
class App extends Component{
render(){
return(
<div>
<Router>
<Switch>
<Route path='/' exact component={()=>(<h1>首页</h1>)}></Route>
<Route path='/form' component={FormCom}></Route>
<Route path='/admin' component={()=>(<h1>admin页</h1>)}></Route>
<Route path='/login' component={()=>(<h1>登录页</h1>)}></Route>
<Route path='/logininfo' component={LoginInfo}></Route>
<Route path='/abc' component={()=>(<h1>abc1</h1>)}></Route>
<Route path='/abc' component={()=>(<h1>abc2</h1>)}></Route>
</Switch>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router,Route,Link,Redirect,Switch} from 'react-router-dom';
function LoginInfo(props){
if(props.location.state.loginState === 'success'){
return <Redirect to='/admin'></Redirect>
}else{
return <Redirect to='/login'></Redirect>
}
}
let FormCom = ()=>{
let pathObj = {
pathname:'/logininfo',
state:{
loginState:'success'
}
}
return(
<div>
<h1>表单验证</h1>
<Link to={pathObj}>登录验证后页面</Link>
</div>
)
}
class ChildCom extends Component{
clickEvent = ()=>{
console.log(this.props);
// go(1) 前进1 go(-1)后退
// goBack 返回
// goForward 前进
// this.props.history.push("/",{state:'传递的值'})
// this.props.history.replace("/",{state:'传递的值'});
this.props.history.go(1);
// this.props.history.goBack();
}
render(){
return(
<div>
<button onClick={this.clickEvent}>跳转到首页</button>
</div>
)
}
}
class App extends Component{
render(){
return(
<div>
<Router>
<Switch>
<Route path='/:id' exact component={(props)=>{console.log(props);return(<h1>首页</h1>)}}></Route>
<Route path='/form' component={FormCom}></Route>
<Route path='/admin' component={()=>(<h1>admin页</h1>)}></Route>
<Route path='/login' component={()=>(<h1>登录页</h1>)}></Route>
<Route path='/logininfo' component={LoginInfo}></Route>
<Route path='/abc' component={()=>(<h1>abc1</h1>)}></Route>
<Route path='/abc' component={()=>(<h1>abc2</h1>)}></Route>
<Route path='/child' component={ChildCom}></Route>
</Switch>
</Router>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
14.Redux
解决React数据管理(状态管理),用于中大型,数据比较庞大,组件之间数据交互的情况下使用。如果你不知道是否需要使用Redux,那么你就不需要用它!
- 解决组件的数据通信。
- 解决数据和交互较多的应用。
Redux只是一种状态管理的解决方案!
- Store: 数据仓库,保存数据的地方。
- State: state是1个对象,数据仓库里的所有数据都放在1个state里。
- Action: 1个动作,触发数据改变的方法。
- Dispatch: 将动作触发成方法。
- Reduce: 是1个函数,通过获取动作,改变数据,生成1个新的state,从而改变页面。
安装Redux
cnpm install redux --save
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
// 用于通过动作,创建新的state
// reducer有2个作用,1初始化数据,第二个就是通过获取动作,改变数据
const reducer = function(state={num:0},action){
console.log(action);
switch (action.type) {
case "add":
state.num++;
break;
case "decrement":
state.num--;
break;
default:
break;
}
return {...state} //相当于对象的copy 变成全新的数据,需要对新的状态进行解构 新版本可以直接返回 return state
}
// 创建仓库
const store = createStore(reducer);
// store里面有dispatch subscibe(监听数据变化) getState replaceReducer
function add(){
// 通过仓库的方法dispatch进行修改数据
store.dispatch({
type:'add',
content: {
id: 1,
msg: 'hello world'
}
})
console.log(store.getState())
}
function decrement(){
// 通过仓库的方法dispatch进行修改数据
store.dispatch({
type:'decrement'
})
console.log(store.getState())
}
// 函数式计数器
const Counter = function (props){
// 获取数据
console.log(store.getState());
let state = store.getState();
return (
<div>
<h1>计数数量:{state.num}</h1>
<button onClick={add}>计数+1</button>
<button onClick={decrement}>计数-1</button>
</div>
)
}
ReactDOM.render(
<Counter />,
document.getElementById('root')
)
// 修改视图(监听数据变化,重新渲染内容)
store.subscribe(()=>{
ReactDOM.render(
<Counter />,
document.getElementById('root')
)
})
15. react-redux
安装
cnpm install react-redux --save
- Provider组件:自动的将store里的state和组件进行关联。
- MapStatetoProps:这个函数用于将store的state映射到组件里的props
- mapdispatchtoProps:将store中的dispatch映射到组件的props里,实现了方法的共享
- connect方法:将组件和数据(方法)进行连接。
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider,connect } from 'react-redux';
class Counter extends React.Component{
render(){
// 计数,通过store的state传给props,直接通过props就可以将state的数据获取
const value = this.props.value;
// 将修改数据的事件或者方法传入到props
const onAddClick = this.props.onAddClick;
// 等同于vuex的mapMutation mapState
return(
<div>
<h1>计数的数量:{value}</h1>
<button onClick={onAddClick}>数字+1</button>
</div>
)
}
}
const addAction = {
type:'add'
}
function reducer(state={num:0},action){
switch(action.type){
case "add":
state.num++;
break;
default:
break;
}
return {...state};
}
const store = createStore(reducer);
// 将state映射到props函数
function mapStateToProps(state){
return {
value:state.num
}
}
// 将修改state数据的方法,映射到props,默认会传入store里的dispatch方法
function mapDispatchToProps(dispatch){
return {
onAddClick:()=>{dispatch(addAction)}
}
}
// 将上面的这2个方法,将数据仓库的state和修改state的方法隐射到组件上,形成新的组件
const App = connect(
mapStateToProps,
mapDispatchToProps
)(Counter)
ReactDOM.render(
<Provider store={store}>
<App></App>
</Provider>,
document.getElementById("root")
)