react全家桶实战

整个目录结构如下:
这里写图片描述

package.json代码如下:

{
  "name": "active",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-redux": "^5.0.7",
    "react-router-dom": "^4.3.1",
    "react-scripts": "1.1.4",
    "react-swipe": "^5.1.1",
    "react-transition-group": "^2.3.1",
    "redux": "^4.0.0",
    "redux-logger": "^3.0.6",
    "redux-promise": "^0.6.0",
    "redux-thunk": "^2.3.0",
    "swipe-js-iso": "^2.0.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "proxy": {
    "/api": {
      "target": "http://localhost:1000/",
      "changeOrigin": true,
      "pathRewrite": {
        "/api": "/"
      }
    }
  },
  "devDependencies": {
    "babel-plugin-transform-decorators-legacy": "^1.3.5"
  }
}

api/home.js(在redux中的action中被引入store/actions/home.js)用来获取首页数据

//获取轮播图数据
function getSilders() {
    //最原始的fetch
    // fetch('api/loop', {method: "post"}).then(function (response) {
    //     // 转换为 JSON
    //     console.log("response")
    //     console.log(response)
    //     return response.json();
    // }).then(function (j) {
    //     console.log('获取到的数据是====');
    //     console.log(j);
    //     return j
    // });
    ////redux-promise的用法:可以将payload中的promise执行,执行后将内容放到action.payload中进行派发:{ type:SET_SLIDERS, patload:[{},{},{},{}] }
    return fetch('api/loop', {method: "post"})
}
export { getSilders }

common/Tab/Tab.js;在app.js中被引入;作为底部导航;切换主页面

import React,{Component} from 'react'
import {Link,NavLink} from 'react-router-dom'

export default class Tab extends Component{
    constructor(){
        super()

    }
    render(){
        return (
            <ul>
                <li><Link to={'/'}> 首页</Link></li>
                <li><Link to={'/lesson'} >课程 </Link></li>
                <li><Link to={'/profile'} > 用户中心</Link></li>

                {/*第二种写法如下:*/}
                {/*<li> <NavLink to={'/'}>首页</NavLink> </li>*/}
                {/*<li> <NavLink to={'/lesson'}>课程</NavLink> </li>*/}
                {/*<li> <NavLink to={'/profile'}>用户中心</NavLink> </li>*/}
            </ul>
        )
    }
}

总结:
1:路由的2种写法NavLink , Link:
2:在当前路由下,再次点击路由切换报警告如下:
这里写图片描述

containers/Home/Homeheader/Homeheader.js;被Home.js被引入

import React, {Component} from 'react'
import logo from '../../../common/img/logo.png'
import './index.css'
import Transition from 'react-transition-group/Transition';

const duration = 10;
//默认样式
const defaultStyle = {
    transition: `opacity ${duration}ms ease-in-out`,
    opacity: 0,
    display:'none'
}

const transitionStyles = {
    entering: {opacity: 0},
    entered: {opacity: 1},
};

export default class Homeheader extends Component {
    constructor() {
        super()
        this.state = {
            isShow: false
        }
    }

    changeShow = () => {
        this.setState({
            isShow: !this.state.isShow
        })
    }
    selectLesson=(e)=>{
        //li元素,当前点击的元素是谁就是谁
        // console.log(e.target)
        // //整个ul元素。事件在谁身上绑着就是谁
        // console.log(e.currentTarget)
        // //获取h5 data属性
        // console.log(e.target.dataset.type)
        //隐藏元素
        this.changeShow()
        this.props.selectCurrentLesson(e.target.dataset.type)
    }

    render() {
        return (
            <div className={'home-header'}>
                <p>
                    <i onClick={this.changeShow} className={'iconfont icon-menu menu'}></i>
                    <img className={'logo'} src={logo} alt=""/>
                </p>
                <Transition in={this.state.isShow} timeout={duration} onEnter={(node)=>{
                    node.style.display='block'
                }}  onExited={(node)=>{
                    node.style.display='none'
                }}>
                    {(state) => (
                        <ul className={'menu-ul'} style={{
                            ...defaultStyle,
                            ...transitionStyles[state]
                        }}
                            onClick={(e)=>{
                                this.selectLesson(e)
                            }}
                        >
                            <li data-type="all">全部课程</li>
                            <li data-type="react">react课程</li>
                            <li data-type="vue">vue课程</li>
                        </ul>
                    )}
                </Transition>

            </div>
        )
    }
}

总结:
1:显示隐藏采用‘react-transition-group’组件
2:官方组件地址:https://github.com/reactjs/react-transition-group
3:html5中的data属性获取:e.target.dataset.type
4:效果图如下:
这里写图片描述

containers/Home/Home.js;路由‘/’的组件

import React, {Component} from 'react'
import './index.css'
import Homeheader from './Homeheader/Homeheader'
import {connect} from 'react-redux'
import HomeSlider from './HomeSlider'
import Loading from '../../components/Loading'
import actions from '../../store/actions/home'
//修饰符的用法待研究
@connect(state => ({...state.home}), actions)
export default class Home extends Component {
    // constructor(){
    //     super()
    // }
    componentWillMount() {
        // 第一种方法:直接ajax
        // fetch('api/loop', {method: "post"}).then(function (response) {
        //     // 转换为 JSON
        //     return response.json();
        // }).then(function (j) {
        //     console.log(j);
        // });
        // 第2种方法:ajax从外部引入
        // getSilders()
        // 第3种方法:采用redux;只有connect装饰器以后才能使用redux中的store;如果redux中存在数据;则不进行请求
        if (this.props.slider.length === 0) {
            this.props.getSliderApi()
        }

    }
    selectCurrentLesson = (val) => {
        // console.log('传给父组件的值是'+val)
        console.log(this.props)
        this.props.updateCurrentLesson(val)
    }
    render() {
        return (
            <div>
                <Homeheader selectCurrentLesson={this.selectCurrentLesson}></Homeheader>
                //解决轮播图不出现的BUG
                {
                    this.props.slider.length>0 ? <HomeSlider lists={this.props.slider}></HomeSlider> : <Loading></Loading>
                }
            </div>
        )
    }
}

containers/Lesson/Lesson.js;路由‘/lesson’的组件

import React,{Component} from 'react'
import './index.less'

export default class Lesson extends Component{
    // constructor(){
    //     super()
    // }
    render(){
        return (
            <div>
            Lesson
            </div>
        )
    }
}

containers/Profile/Profile.js;路由‘/Profile’的组件

import React,{Component} from 'react'
import './index.less'

export default class Profile extends Component{
    // constructor(){
    //     super()
    // }
    render(){
        return (
            <div>
            Profile
            </div>
        )
    }
}

store/actions/home.js;在Home.js中被引入;

import * as Types from '../action-types'
import { getSilders } from '../../api/home'


let actions={
    //更新当前选择的课程
    updateCurrentLesson(lesson){
        return {
            type:Types.SET_CURRENT_LESSON,
            lesson
        }
    },
    getSliderApi(){
        // redux-thunk
        return function (dispatch,getState) {
            //redux-promise的用法:可以将payload中的promise执行,执行后将内容放到action.payload中进行派发:{ type:SET_SLIDERS, patload:[{},{},{},{}] }
            dispatch({type:Types.SET_SLIDERS ,payload:getSilders().then(function (response) {
                    // 转换为 JSON
                    console.log("response")
                    console.log(response)
                    if(response.status==200){
                        return response.json();
                    }else{
                        alert('服务器挂掉了')
                    }
                }).then(function (j) {
                    // console.log('获取到的数据是====');
                    // console.log(j);
                    return j
                })})
        }

    }
}
export default  actions

store/reducers/home.js;在下面的index.js中被引入;

//一个页面一个reducer
import * as Types from '../action-types'
let initState={
    currentLesson:'all',
    slider:[]
}
function home(state=initState, action) {
    switch (action.type){
        case Types.SET_CURRENT_LESSON:
            return {
                ...state,
                currentLesson:action.lesson
            }
        case Types.SET_SLIDERS:
            return {
                ...state,
                slider:action.payload
            }
        default:{
            return state
        }


    }
}
export default home

store/reducers/index.js;在store中index.js中被引入;

import { combineReducers } from 'redux'
import home from './home'
export default  combineReducers({
    home
})

store/action-types.js;在store中的各种JS文件中引入;

//设置当前课程
export const SET_CURRENT_LESSON='set_current_lesson'
//设置轮播图数据
export const SET_SLIDERS='set_sliders'

store/index.js;被最外层的index.js引入

import { createStore , applyMiddleware } from 'redux'
import reduxLogger from 'redux-logger'
import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'
import reducer from './reducers/index'


let store= createStore(reducer,applyMiddleware(reduxLogger,reduxThunk,reduxPromise))
window._store=store; //为了测试用
export default store

总结:
写redux的逻辑
1:action-types
2:reducer
3:action
4:组件中调用action; dispatch
这里写图片描述

架构最外层的index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
//配置路由组件
import {HashRouter as Router,Route, Switch} from 'react-router-dom'
import Home from './containers/Home/Home'
import Profile from './containers/Profile/Profile'
import Lesson from './containers/Lesson/Lesson'

import {Provider} from 'react-redux'
import store from './store/index'

import './common/reset.css'

ReactDOM.render(
    <Provider store={store}>
        <Router>
            <App>
                <Switch>
                    <Route path='/' exact={true} component={Home}></Route>
                    <Route path='/lesson' component={Lesson}></Route>
                    <Route path={'/profile'} component={Profile}></Route>
                </Switch>
            </App>
        </Router>
    </Provider>
    , document.getElementById('root'));
registerServiceWorker();

架构最外层的app.js

import React, { Component } from 'react';
import Tab from './common/Tab/Tab'


class App extends Component {
  render() {
    return (
        <div>
            {this.props.children}
            <Tab></Tab>
        </div>

      // <div className="App">
      //     {this.props.children}
      //
      // </div>
    );
  }
}

export default App;

HomeSlider.js

import React,{Component} from 'react'
//轮播图组件
import ReactSwipe from 'react-swipe';
export default class HomeSlider extends Component{
    constructor(){
        super()
        this.state={
            index:0
        }
    }

    render(){
        let _this=this
        let opts={continuous: true,
            speed: 400,
            auto: 1000,
            callback: function(index, elem) {
                _this.setState({
                    index:index
                })
            },
        }
        return (
            <div className={'loop'}>
                <ReactSwipe className="carousel" swipeOptions={opts}>
                    {
                        this.props.lists.map((item, index) => {
                            return (
                                <div key={index}><img src={item.url} alt=""/></div>
                            )

                        })
                    }
                </ReactSwipe>
                <div className={'slider-dots'}>
                    {
                        this.props.lists.map((item, index) => {
                            return (
                                <span key={index} className={this.state.index===index ? 'active': ''}></span>
                            )

                        })
                    }

                </div>
            </div>

        )
    }
}

1:轮播图插件

npm install react swipe-js-iso react-swipe

轮播图插件swipe-js-iso拖动一次后不再轮播的BUG修复
这里写图片描述

 delay = options.auto || 0;

猜你喜欢

转载自blog.csdn.net/weixin_38788947/article/details/80716571