react学习

GeGeZZ -
react学习
react框架库react-router 路由pubsub 消息管理redux 状态管理ant-design UI库要点

jsx 最后翻译过来就是React.createElement方法

createElement(element,options,content)

elment 元素名称

options 属性对象

content 属性里面的内容

demo: createElement('h1',{id:'test'},'hello react')

虚拟dom解析后是Object

jsx注意事项

变量用一个大括号括起来;如果引用class样式,需要用className定义的jsx不需要引号,如果有多层,用小括号括起来内联样式style要用对象的形式,且属性要用驼峰命名只允许一个根标签标签必须闭合

自定义组件以大写字母开头

const name='zhangsan'
const Rdom=(
    <div>
        <h1 className='test' style={{fontSize:'20px'}}>{name}<h1>
        <h1 className='test' style={{fontSize:'20px'}}>{name}<h1>
    </div>
)

react创建函数组件

函数名大写,且要有返回值

引用需要用标签,且标签要有闭合

function Demo(){
    return <h1>demo test</h1>
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

react创建类组件

自定义组件类必须继承React.Component类组件里面必须定义render方法,并且需要返回值调用组件,渲染到页面,组件是命名是大写开头调用组件会new一个组件,然后调用render方法,且类里面this指向的是组件创建的实力对象,也叫组件实力对象。
class MyComponent extends React.Component{
       render(){
           return (
               <h1>test</h1>
           )
       }
   }
   ReactDOM.render(<MyComponent/>,document.getElementById('test'))

react事件以及state

1.节点绑定事件需要用on+事件名,需要驼峰命名

2.事件的回调函数需要用bind重新绑定,否则this会丢失,或者用箭头函数赋值给属性,绑定该属性

3.state对象里面需要定义需要变化的状态(属性)

4.更改state里面的状态,需要用this.setState方法

5.自定义方法需要用箭头函数赋值给属性,或者用bind方法绑定this

 class ChangeWeather extends React.Component{
     constructor(){
         super()
         this.state={
             flag:true
         }
         this.changeFlag=this.changeFlag.bind(this)
     }
     render(){
         const {flag}=this.state
         return (
             <h1 onClick={this.changeFlag}>今天天气很{flag?'好':'不好'}</h1>
         )
     }
     changeFlag(){
        this.setState({
            flag:!this.state.flag
        })
     }
 }
 ReactDOM.render(<ChangeWeather/>,document.getElementById('test'))
  //上面例子精简版
class ChangeWeather extends React.Component {
        state = { flag: true }
        render() {
            const { flag } = this.state
            return (
                <h1 onClick={this.changeFlag}>今天天气很{flag ? '好' : '不好'}</h1>
            )
        }
        changeFlag = () => {
            this.setState({
                flag: !this.state.flag
            })
        }
    }
    ReactDOM.render(<ChangeWeather />, document.getElementById('test'))

react里面的props

1.通过标签传递属性,可以通过扩展运算符进行传递

2.组件里面通过props引用

3.如果是变量需要用大括号的形式进行传递

 class ChangeWeather extends React.Component{
       render (){
           return (
               <div>
                    {this.props.title}
                    {this.props.age}
                    {this.props.obj.test}
                    {this.props.test}
                </div>
           )
       }
   }
    const obj={
        test:123
    }
    ReactDOM.render(<ChangeWeather title="title" age={18} obj={obj} {...obj}/>, document.getElementById('test'))

props的属性限制以及默认值

1.16.x版本之前是用React.propsType.xxxx进行限制,之后的版本是需要引入propstype.js,然后再类的propTypes属性上面进行定义

2.限制属性的时候,直接定义到类上面进行限制

3.如果是函数限制传的类型为func,而不是function

4.如果没有传值,需要给默认值,需要再类的defaultProps属性上面进行定义默认值

5.定义的这些规则需要咋渲染之前进行定义

6.定义的属性规则也可以用静态属性进行定义static

 //直接定义到类上面
class ChangeWeather extends React.Component{
       
       render (){
           return (
               <div>
                    {this.props.title}
                    {this.props.age}
                    {this.props.obj.test}
                    {this.props.test}
                </div>
           )
       }
   }
        //需要再渲染之前进行规则定义,在类的propTypes上面进行定义
       ChangeWeather.propTypes = {
            title: PropTypes.string.isRequired,
            fun:PropTypes.func.isRequired//函数要用func这个类型
        }
        //默认值需要再类的defaultProps属性上面进行定义
        ChangeWeather.defaultProps={
            title:"fdsaf",
            fun:function(){}
            
        }
    const obj={
        test:123,
       
    }
    ReactDOM.render(<ChangeWeather title="test" age={18} obj={obj} {...obj}/>,             document.getElementById('test'))
  //用静态属性的方式
class ChangeWeather extends React.Component {
        static propTypes = {
            title: PropTypes.string.isRequired,
            fun: PropTypes.func.isRequired
        }
        static defaultProps = {
            title: "fdsaf",
            fun: function () { }

        }
        render() {
            return (
                <div>
                    {this.props.title}
                    {this.props.age}
                    {this.props.obj.test}
                    {this.props.test}
                </div>
            )
        }
    }

    const obj = {
        test: 123,

    }
    ReactDOM.render(<ChangeWeather title="test" age={18} obj={obj} {...obj} />, document.getElementById('test'))

react里面的ref

1.在节点里面通过ref属性进行绑定

2.在逻辑里面通过refs接受

3.接受的ref是一个真实节点

  class Test extends React.Component{

        render(){
            return (
                <div>
                    <input ref="input1" type="text"/>
                    <button onClick={this.btnClick}>点击</button>
                </div>
            )
        }
        btnClick=() => {
            const {input1}=this.refs
            alert(input1.value)
        }
    }
    ReactDOM.render(<Test/>,document.getElementById('test'))

4.ref通过回调方式获取

 /* ref  回调函数的形式 */
   class Reftest extends React.Component{
       inputel=null
       btnClick=() => {
           alert(this.inputel.value)
       }
       render(){
           return (
               <div>
                <input  type="text" ref={(el)=>{this.inputel=el}}/>
                <button onClick={this.btnClick}>点击 </button>
               </div>
           )
       }
    }
    ReactDOM.render(<Reftest/>,document.getElementById('test'))
ref如果用内联的方式进行获取,在数据更新的 时候会调用两次回调函数,第一次会给回调函数传null第二次会传当前的节点。解决办法为直接用绑定的回调函数。只会调用一次回调函数,不会调用多次。但是内联和绑定的方式不会有任何影响。开发中一般用内联的方式进行开发。

ref的创建方式三,通过React.createRef的方式创建。该函数调用后会返回一个对象,keycurrent的对象,值为节点。有多少个ref就要调用几次前面的函数。官方推荐写法

 /* ref  React.createRef的方式 */
   class Test extends React.Component{
       //有多少个就要创建多少个
       refstest=React.createRef();
       btnclick=()=>{
           console.log(this.refstest.current.value);
           
       }
       render(){
           return (
               <div>
                   //这里了是需要绑定上面返回的属性
                    <input ref={this.refstest} type="text"/>
                    <button onClick={this.btnclick}>点击</button>
                </div>
           )
       }
   }
   ReactDOM.render(<Test/>,document.getElementById('test'))
react的非受控组件所有的表单组件现用现取的组件。取值需要用到ref

react的受控组件

所有的输入框的值都存到状态里面,类似vue的双向绑定

    /* 受控组件*/
    class Test extends React.Component {
        state = {
            name: ''
        }
        getTestValue = (e) => {
            this.setState({
                name: e.target.value
            })
        }
        handleClick = () => {
            console.log(this.state.name);
        }
        render() {
            return (
                <div>
                    <input type="text" onChange={this.getTestValue} />
                    <button onClick={this.handleClick}>点击</button>
                </div>
            )
        }
    }
    ReactDOM.render(<Test />, document.getElementById('test'))

函数的柯力化以及高阶函数

1.高阶函数:

1.1函数返回一个函数

1.2 函数传参,该参数为一个函数

2.柯力化函数:

2.1函数返回函数,最后返回的函数一起处理前面函数的参数。

    class Test extends React.Component {
        state = {
            name: ''
        }
        getTestValue = (name) => {
          return (event) => {
            console.log(name,event);
          }
        }
        handleClick = () => {
            console.log(this.state.name);
        }
        render() {
            return (
                <div>
                    <input type="text" onChange={this.getTestValue('test')} />
                    <button onClick={this.handleClick}>点击</button>
                </div>
            )
        }
    }
    ReactDOM.render(<Test />, document.getElementById('test'))

react的声明周期

ReactDOM.unmountComponentAtNode(el)卸载组件

//只要一获取值就销毁组件
class Test extends React.Component {
        getValue(key, e) {
            ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        }
        render() {
            return (
                <div>
                    <input type="text" onChange={(e) => { this.getValue('userName', e) }} />
                </div>
            )
        }
    }
    ReactDOM.render(<Test />, document.getElementById('test'))

react组件生命周期

1.componentDidMount//组件挂在时调用,只调用一次

2.componentWillUnmount //组件即将销毁

   /* 组件生命周期演示 */
    class Test extends React.Component {
        state = {
            count: 1
        }
        timer = null;
        render() {
            const { count } = this.state;
            return (
                <div>
                    <div> 数字:{count}</div>
                    <div onClick={this.removeTimer}>点击我</div>
                </div>
            )
        }
        // 组件挂载
        componentDidMount() {
            this.timer = setInterval(() => {
                let { count } = this.state;
                count += 1;
                this.setState({
                    count,
                })
            }, 200)
        }
        // 组件即将卸载
        componentWillUnmount() {
            clearInterval(this.timer)
        }
        removeTimer = () => {
            // 移除当前组件
            ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        }
    }
    ReactDOM.render(<Test />, document.getElementById('test'))

子组件生命周期执行顺序

constructorcomponentWillMountrendercomponentDidMountcomponentWillUnmount

getSnapshotBeforeUpdate

   //初始化的声明周期执行顺序
class Demo extends React.Component {
        constructor(props) {
            super(props)
            console.log('constructor');
            this.state = {
                count: 1
            }
        }

        componentWillMount() {
            console.log('componentWillMount');
        }
        componentWillUnmount() {
            console.log('componentWillUnmount');
        }
        render() {
            console.log('render');
            const { count } = this.state
            return (
                <div>
                    <div>展示的count:{count}</div>
                    <div onClick={this.btnClick}>点击我</div>
                    <div onClick={this.remove}>卸载组件</div>
                </div>
            )
        }
        remove = () => {
            ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        }
        btnClick = () => {
            let { count } = this.state
            count += 1;
            this.setState({
                count,
            })
        }
    }
    ReactDOM.render(<Demo />, document.getElementById('test'))

     class A extends React.Component{
        state={
            count:1
        }
        render(){
            console.log('render');
            const {count}=this.state
            return (
                <div>
                    <B name={count}/>
                    <div>count {count}</div>
                    <div onClick={this.btnClick}>点击我</div>
                </div>
            )
        }
        // 跟新的时候会走这个生命周期,必须要返回true或者false,若返回false就不会执行后面的声明周期
        shouldComponentUpdate(){
            console.log('shouldComponentUpdate');
            return true
        }
        componentWillUpdate(){
             console.log('componentWillUpdate');
        }
        componentDidUpdate(){
             console.log('componentDidUpdate');

        }
        btnClick=() => {
            let {count}=this.state
            count+=1;
            this.setState({
                count ,
            })
        }
    }
    class B extends React.Component{
        //父组件传递的props更新了才会调用这个生命周期,初次不会调用该生命周期
        componentWillReceiveProps(){
            console.log('componentWillReceiveProps');
        }
        render(){
            return (
                <div>{this.props.name}</div>
            )
        }
    }

    ReactDOM.render(<A />, document.getElementById('test'))
  class Demo extends React.Component {
        state = {
            arr: []
        }
        componentDidMount() {
            setInterval(() => {
                const { arr } = this.state;
                this.setState({
                    arr: ['新闻' + (arr.length + 1), ...arr]
                })
            }, 1000)
        }
        // 新的声明周期,在更新前执行,可以像componentDidUpdate声明周期里面传递一个参数
        getSnapshotBeforeUpdate() {
            return this.refs.list.scrollHeight
        }
        componentDidUpdate(prepops, prestates, scrollheight) {
            console.log(this.refs.list.scrollHeight, '...', scrollheight);

            this.refs.list.scrollTop += this.refs.list.scrollHeight - scrollheight

        }
        render() {
            return (
                <div>
                    <div className="list" ref="list">
                        {
                            this.state.arr.map((item, index) => {
                                return (
                                    <div key={index} className="li">{item}</div>
                                )
                            })
                        }
                    </div>
                </div>
            )
        }
    }
    ReactDOM.render(<Demo />, document.getElementById('test'))

react diff算法

通过数据渲染成虚拟dom通过key值去判断是否存在相同的值,如果key值相同,判断对应的内容是否相同,若内容相同复用原来的dom,如果不相同,创建新的dom渲染到页面,如果key值不相同直接生成dom渲染到页面使用index作为key值存在的问题:可能会使效率低下(旧dom可能复用不了),页面错位(若数据存在逆反操作)。

react脚手架

1.全局安装npm i -g create-react-app

2.进入要创建项目的目录控制台执行create-react-app projectName即创建成功

样式模块化

1.利用文件名XXX.module.css,引入方式import hello from './xxx.module.css'在用的地方用hello.className的形式进行

2.利用less进行嵌套

react组件父子通信

1.父传子利用props进行传递

2.子传父利用props给子组件传递函数,子组件通过props接收该函数,然后调用,且可以传值。

公共用的状态放到父组件中(状态提升),状态定义到哪里,对应的方法就对应在哪个组件中

解决跨域

利用package.jsonproxy字段进行代理。请求的时候用本地的域名加端口号加接口名进行请求

//pageage.json   
"development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "proxy":"http://localhost:5000"
 //请求的地址,端口号为本地的3000,3000代理到服务器为端口号5000的服务器
axios.get('http://localhost:3000/students')
      .then((res) => {
        console.log(res.data)
      })
  }

2.在项目目录src/下新建setupProxy.js文件,然后写入如下代码:

const proxy = require('http - proxy - middleware');

module.exports = function (app) {
    app.use(proxy('/api', { 
        target: 'http://localhost:5000',
        secure: false,
        changeOrigin: true,
        pathRewrite: {
        "^/api": ""
    },
    }));
};

兄弟组件之间的通信,利用pubsubjs

安装pubsub-js

yarn add pubsub-js

引入pubsubjs并在传递数据的组件中进行消息发布,在需要获取数据的组件中进行消息订阅

  componentDidMount() {
        this.id=Pubsub.subscribe('getData', (name, stateObj) => {
            this.setState(stateObj)
         })
    }
componentWillUnmount(){
    pubsub.undescribe(this.id)
}
request=()=>{
        axios.get(
            'http://localhost:3000/xxx'
        )
            .then((res) => {
                Pubsub.publish('getData',{
                    loading: false,
                    users: res.data.items,
                    err:false
                })
            })
            .catch((err) => {
                Pubsub.publish('getData',{
                    loading: false,
                    err:err.message
                })
             })
}

react路由

1.安装react-router-dom

yarn add react-router-dom

引入必要组件

import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import About from './components/About'
import Home from './components/Home'
export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* <a className="list-group-item active" href="./about.html">About</a>
              <a className="list-group-item" href="./home.html">Home</a> */}
              <Link to="/about" className="list-group-item">About</Link>
              <Link to="/home" className="list-group-item">Home</Link>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* <h3>我是About的内容</h3> */}
                <Route path='/about' component={About}/>
              <Route path='/Home' component={ Home}/>
            </div>
          </div>
        </div>
        </div>
        </div>
    )
  }
}
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'
import './index.css';
import App from './App';

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
通过this.props.children可以获取标签体内容switch组件包裹路由只会匹配第一个匹配到 的路由进行渲染对应组件

react多级路由导致样式丢失问题

public文件下的index.html文件引入文件路径修改为/绝对路径的这种形式,或者用%PUBLIC_URL%这种形式进行引入。或者将BrowserRouter组件换成HashRouter组件

`Router组件中的exact`属性用于精准匹配路由

 <Route exact path='/home' component={Home} />
Redirect组件用于没有匹配到路径的时候,调转到默认路由,一般放到注册组件最后Switch组件用于匹配到第一个路由就不往后面进行匹配NavLink用于路由高亮显示,可以自定义className

Link用于路由跳转

  <Switch>
      <Route  path='/about/about' component={About} />
      <Route exact path='/home' component={Home} />
      <Redirect to="/home"></Redirect>
   </Switch>

嵌套路由

1.子组件注册组件需要带上父组件的path

父组件
import React, { Component } from 'react'
import { Switch, Route ,Redirect} from 'react-router-dom'
import About from './pages/About'
import Home from './pages/Home'
import MyNavLink from './components/MyNavLink'
import './App.css'
export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              <MyNavLink to="/about" >About</MyNavLink>
              <MyNavLink to="/home" >Home</MyNavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                <Switch>
                  <Route  path='/about' component={About} />
                  <Route  path='/home' component={Home} />
                  <Redirect to="/home"></Redirect>
                </Switch>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}
子组件
import React from "react";
import Message from "./Message";
import News from "./News";
import MyNavLink from "../../components/MyNavLink";
import { Switch, Route,Redirect } from 'react-router-dom'
export default class Home extends React.Component {
    render() {
        console.log(this.props);
        return (
            <div>
                <h2>Home组件内容</h2>
                <div>
                    <ul className="nav nav-tabs">
                        <li>
                            <MyNavLink to="/home/news">News</MyNavLink>
                            
                        </li>
                        <li>
                            <MyNavLink to="/home/message">message</MyNavLink>
                        </li>
                    </ul>
                    <Switch>
                        <Route path="/home/news" component={News}></Route>
                        <Route path="/home/message" component={Message}></Route>
                        <Redirect to="/home/message"></Redirect>
                    </Switch>
                </div>
            </div>
        )
    }
}

路由组件传参

params传参

1.跳转的地方需要进行参数按顺序传递

2.注册的地方需要进行按顺序进行接收

3.子组件通过this.props.match.params获取

import React, { Component } from 'react'
import { Route } from 'react-router';
import { Link } from 'react-router-dom';
import MsgDetail from './MsgDetail';
export default class Message extends Component {
    state = {
        students: [{
            id: 1,
            name: '张三',
            age: 12
        }, {
            id: 2,
            name: '李四',
            age: 13
        }, {
            id: 3,
            name: '王二',
            age: 14
        }]
    }
    render() {
        const { students } = this.state;
        return (
            <div>
                <ul>
                    {
                        students.map((item) => {
                            return (
                                <li>
                                    <Link to={`/home/message/msgdetail/${item.name}/${item.age}`} key={item.id}>{item.name}</Link>
                                </li>
                            )
                        })
                    }

                </ul>
                <hr />
                <Route path='/home/message/msgdetail/:name/:age' component={MsgDetail}/>
                
            </div>
        )
    }
}

4.子组件进行获取

import React, { Component } from 'react';

class MsgDetail extends Component {
    render() {
        console.log(this.props);
        const { name, age } = this.props.match.params;
        return (
            <div>
                {name}----{ age}
            </div>
        );
    }
}

export default MsgDetail;

search传参(类似vue里面的query

1.在跳转的地方进行传递

2.在子组件的this.props.location.search进行取值

3.需要将search参数进行转换成对象的形式

import React, { Component } from 'react'
import { Route } from 'react-router';
import { Link } from 'react-router-dom';
import MsgDetail from './MsgDetail';
export default class Message extends Component {
    state = {
        students: [{
            id: 1,
            name: '张三',
            age: 12
        }, {
            id: 2,
            name: '李四',
            age: 13
        }, {
            id: 3,
            name: '王二',
            age: 14
        }]
    }
    render() {
        const { students } = this.state;
        return (
            <div>
                <ul>
                    {
                        students.map((item) => {
                            return (
                                <li>
                                    <Link to={`/home/message/msgdetai?name=${item.name}&age=${age}`} key={item.id}>{item.name}</Link>
                                </li> 
                            )
                        })
                    }

                </ul>
                <hr />
                <Route path='/home/message/msgdetail' component={MsgDetail} />

            </div>
        )
    }
}

4.子组件进行获取

import React, { Component } from 'react';
import qs from 'querystring'
class MsgDetail extends Component {
    render() {
        console.log(this.props);
        const { search } = this.props.location.search
        const { name,age}=qs.parse(search.slice(1))
        return (
            <div>
                {name}----{ age}
            </div>
        );
    }
}

export default MsgDetail;

state传参

1.路由跳转的地方需要通过对象的方式进行传递,需要包含path以及state属性。state属性是一个对象

2.子组件通过this.props.locaiton.state进行获取

3.state传递的参数在地址栏上面是看不到的,上面的两种都是在地址栏上面看的到的。

import React, { Component } from 'react'
import { Route } from 'react-router';
import { Link } from 'react-router-dom';
import MsgDetail from './MsgDetail';
export default class Message extends Component {
    state = {
        students: [{
            id: '00122',
            name: '张三',
            age: 12
        }, {
            id: '00233',
            name: '李四',
            age: 13
        }, {
            id: '003432423',
            name: '王二',
            age: 14
        }]
    }
    render() {
        const { students } = this.state;
        return (
            <div>
                <ul>
                    {
                        students.map((item) => {
                            return (
                                <li key={item.id}>
                                    <Link  to={{ path: '/home/message/msgdetail', state: { name: item.name, age: item.age } }}>{item.name}</Link>
                                </li>
                            )
                        })
                    }

                </ul>
                <hr />
                <Route path='/home/message/msgdetail' component={MsgDetail} />

            </div>
        )
    }
}

4.子组件进行接收

import React, { Component } from 'react';
import qs from 'querystring'
class MsgDetail extends Component {
    render() {
        console.log(this.props);
        
        const { name,age}=this.props.location.state
        return (
            <div>
                {name}----{ age}
            </div>
        );
    }
}

export default MsgDetail;

编程式组件导航

通过this.props.historyapi进行跳转

 jump = (name,age) => {
        this.props.history.push('/home/message/msgdetail', { name, age } )
    }

withRouter 若想在一般组件中使用路由跳转的api可以引入withRouter方法

import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
class Header extends Component {
    goBack = () => { 
        this.props.history.goBack()
    }
    goFowrd = () => { 
        this.props.history.goForward()
    }
    render() {
        return (
            <div className="page-header">
                <h2>React Router Demo</h2>
                <button onClick={this.goBack}>回退</button>
                <button onClick={this.goFowrd}>前进</button>
            </div>
        )
    }
}
export default withRouter(Header)
antd按需引入,可参考地址https://3x.ant.design/docs/react/use-with-create-react-app-cn中的高级配置

redux

1.安装redux

yarn add redux

2.创建redux文件夹,在该文件下创建store文件,action文件,reducer文件,constent文件

1632151830939

​ 3.store.js

import { createStore } from "redux";
import countRedux from './count_redux'
export default createStore(countRedux)

4.count_action.js

import {ADD,DESC } from './constent'
export const add = (data) => ({
    type: ADD,
    data
})
export const desc = (data) => ({
    type: DESC,
    data
})

5.count_redux.js

import { ADD,DESC} from './constent'
export default function (preState=0, action) {
    const { type,data}=action
    switch (type) {
        case ADD:
            return preState + data
        case DESC:
            return preState - data

        default:
           return preState
    }
 }

6.constent.js

export const ADD='add'
export const DESC='desc'

7.index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
import store from './redux/store'
ReactDOM.render(<App></App>, document.getElementById('root'))
store.subscribe(() => {
    ReactDOM.render(<App></App>, document.getElementById('root'))
})

异步action

1.引入并安装redux-thunk

2.利用中间件传入thunk

3.action返回的是一个函数,该函数会带有dispatch函数参数

代码如下:

store.js

import { createStore, applyMiddleware  } from "redux";
import countRedux from './count_redux'
import thunk from 'redux-thunk'
export default createStore(countRedux, applyMiddleware(thunk))

action.js

export const  addAsync = (data, time) => {
    //返回一个带有dispatch的参数的函数
    return (dispatch) => {
        setTimeout(() => {
            dispatch(add(data))//调用同步action
        },time)
    }
}

react-redux

1.引入并安装

yarn add react-redux

​ 2.创建容器组件,用于操作状态以及包裹ui组件(链接ui组件以及redux)

      // 引入要展示的UI组件
import countUi from '../../components/Count'
// 用于连接UI组件以及redux
import { connect } from 'react-redux'
// 引入action
import { add, desc, addAsync } from '../../redux/count_action'
// 将store里面的状态映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
function mapStateToProps(state) { 
    return {
        count: state
    }
}
// 将store里面的操作状态的方法映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
function mapDispatchToProps(dispatch) {
    return {
        add: (num) => {
            dispatch(add(num))
        },
        desc: (num) => {  
            dispatch(desc(num))
        },
        addasync: (num, time) => {
            dispatch(addAsync(num,time))
        }
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(countUi)     
通过props传递给子组件store

UI组件的优化写法

// 引入要展示的UI组件
import countUi from '../../components/Count'
// 用于连接UI组件以及redux
import { connect } from 'react-redux'
// 引入action
import { add, desc, addAsync } from '../../redux/count_action'
// 将store里面的状态映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
/* function mapStateToProps(state) { 
return {
    count: state
}
} */
// 将store里面的操作状态的方法映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
/* function mapDispatchToProps(dispatch) {
return {
    add: (num) => {
        dispatch(add(num))
    },
    desc: (num) => {
        dispatch(desc(num))
    },
    addasync: (num, time) => {
        dispatch(addAsync(num,time))
    }
}
} */
// export default connect(mapStateToProps, mapDispatchToProps)(countUi)

// 优化写法
export default connect(
state => ({ count: state }),
//这里可以返回一个简单对象,内部可以自动分发action
{
    add: add,
    desc: desc,
    addasync: addAsync
}
)(countUi)

5.引入react-redux可以不用手动监听转态变化

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
// import store from './redux/store'
ReactDOM.render(<App></App>, document.getElementById('root'))
// 引入react-redux后可以不用手动监听状态变化
// // 监测redux转态改变,redux状态改变会重新渲染页面
// store.subscribe(() => {
//     ReactDOM.render(<App></App>, document.getElementById('root'))
// })

6.利用react-reduxprovider传递store

import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
import store from './redux/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
    <App />
</Provider >
, document.getElementById('root'))

多个reducer利用combineReducers函数合并

import { createStore, applyMiddleware ,combineReducers } from "redux";
import countRedux from './reducers/count_redux'
import person from "./reducers/person";
import thunk from 'redux-thunk'

export default createStore(
  // 多个reducer用combineReducers合并
  combineReducers({
    count: countRedux,
    person: person,
  }),
  applyMiddleware(thunk)
);

reducer必须是一个纯函数,即传入的什么参数,就返回改参数,且传入的参数不能被改写。如果传入的是对象或者是数组。返回的值也应该是一个对象或数组,不可以在原有的对象或数组中进行操作再返回

import { ADD_PERSON } from "../constent";
export default function (prestate=[],action) { 
    const { type, data } = action;
    switch (type) {
      case ADD_PERSON:
        return [data,...prestate]
      default:
       return []
    }

}

redux配置的开发者工具

谷歌浏览器安装redux-devtools在项目中安装redux-devtools-extension

store.js中进行如下配置

import { createStore, applyMiddleware  } from "redux";
import thunk from 'redux-thunk'
import { composeWithDevTools } from "redux-devtools-extension";
import reducers from "./reducers";
export default createStore(
  // 多个reducer用combineReducers合并
  reducers,
  composeWithDevTools(applyMiddleware(thunk))
);

打包后的文件如何在本地启动

1.全局安装serve插件

npm install -g serve

进入打包的文件目录运行serve命令

特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

Tags 标签

javascript前端react.jshtml5es6

扩展阅读

加个好友,技术交流

1628738909466805.jpg