0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

react-redux使用及实现、router简介

修罗 2020-11-24 1786 0条评论 react

首页 / 正文

react-redux使用及实现、router简介

1606211805828.png

资源:

React中文网:https://react.docschina.org/

React Redux API:https://www.redux.org.cn/docs/react-redux/api.html

react-redux github:https://github.com/reduxjs/react-redux

react-router英文文档:https://reactrouter.com/web/guides/quick-start

react-router中文文档:http://react-router.docschina.org/

1、使用react-redux

npm install react-redux

提供了两个api

  1. Provider 为后代组件提供store
  2. connect 为组件提供数据和变更方法

API

(1)<Provide store>

<Provider store={store}>
 <App />
</Provider>

<Provide store>使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在<Provide>中才能使用 connect() 方法。

(2)connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

// 参数1
const mapStateToProps = state => ({count: state})

// 参数2
const mapDispatchToProps = {
 add: () => ({ type: "add" }),
 minus: () => ({ type: "minus" });
};

export default connect(
 mapStateToProps, //状态映射 mapStateToProps
 mapDispatchToProps, //派发事件映射
)(组件);

第二个参数为函数的情况

import {bindActionCreators} from "redux";

(dispatch) => {
  let res = {
    add: () => ({ type: "ADD" }),
    minus: () => ({ type: "MINUS" }),
  };
  res = bindActionCreators(res, dispatch);

  let aadd = () => {
    setTimeout(() => dispatch({ type: "ADD" }), 1000);
  };
  return { dispatch, aadd, ...res };
}

连接 React 组件与 Redux store。

返回一个新的已与 Redux store 连接的组件类。

参数 1

  • 函数:mapStateToProps(state, [ownProps])

该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。

如果定义该参数,组件将会监听 Redux store 的变化,否则不监听。

ownProps是当前组件自身的props,如果指定了,那么只要组件接收到新的props,mapStateToProps 就会被调用,mapStateToProps 都会被重新计算,mapDispatchToProps 也会被调用。注意性能!

参数 2

  • 对象或函数:mapDispatchToProps(dispatch, [ownProps])

如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。

如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中 dispatch方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。

如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象。

ownProps是当前组件自身的props,如果指定了,那么只要组件接收到新的 props,mapDispatchToProps 就会被调用。注意性能

参数 3

  • 函数:mergeProps(stateProps, dispatchProps, ownProps)

如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据, 或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。

具体使用

全局提供store,index.js

import React from "react";
import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import store from "./store/";

// 把Provider放在根组件外层,使用组件能获得store
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

获取状态数据,ReactReduxPage.js

import React, { Component } from "react";
import { connect } from "react-redux";
class ReactReduxPage extends Component {
  render() {
    const { num, add, minus, asyAdd } = this.props;
    return (
      <div>
        <h1>ReactReduxPage</h1>
        <p>{num}</p>
        <button onClick={add}>add</button>
        <button onClick={minus}>minus</button>
      </div>
    );
  }
}
const mapStateToProps = (state) => {
  return {
    num: state,
  };
};
const mapDispatchToProps = {
  add: () => {
    return { type: "add" };
  },
  minus: () => {
    return { type: "minus" };
  },
};
export default connect(
  mapStateToProps, //状态映射 mapStateToProps
  mapDispatchToProps //派发事件映射
)(ReactReduxPage);
connect中的参数:state映射和事件映射

详细使用

import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";

// connect帮组子组件与store链接,其实就是高阶组件,这里返回的是一个新的组件
export default connect(
  // 1.0、mapStateToProps Function (state, ownProps)
  (state) => ({ count: state }),
  // !谨慎使用ownProps,如果它发生变化,mapStateToProps就会执行,里面的state会被重新计算,容易影响性能
    
  // 1.1、(state, ownProps) => {
  //  console.log("ownProps", ownProps); //sylog
  //  return {
  //   count: state
  //  };
  // }
    
  // 2.0、Object mapDispatchToProps 如果不定义 默认把props注入组件
  // 如果是对象的话,原版的dispatch就没有被注入了
  // {
  //     add: () => ({type: "ADD"})
  // }
    
  // 2.1、 Function (dispatch,ownProps)
  // !谨慎使用ownProps,如果它发生变化,mapDispatchToProps就会执行,容易影响性能
  // (dispatch, ownProps) => {
  //     console.log("ownProps", ownProps);
  // } //sylog
  (dispatch) => {
    let res = {
      add: () => ({ type: "ADD" }),
      minus: () => ({ type: "MINUS" }),
    };
    res = bindActionCreators(res, dispatch);

    let aadd = () => {
      setTimeout(() => dispatch({ type: "ADD" }), 1000);
    };
    return { dispatch, aadd, ...res };
  },
    
  // 3、mergeProps Function
  // 如果指定了这个参数,`mapStateToProps()` 与`mapDispatchToProps()` 的执行结果和组件自身的`props` 将传入到这个回调函数中。
  (stateProps, dispatchProps, ownProps) => {
    console.log("mergeProps", stateProps, dispatchProps, ownProps); //sy-log
    return { omg: "omg", ...stateProps, ...dispatchProps, ...ownProps };
  }
)(
  class ReactReduxPage extends Component {
    render() {
      console.log("props", this.props); //sylog
      const { count, dispatch, add, minus } = this.props;
      return (
        <div>
          <h3>ReactReduxPage</h3>
          <p>{count}</p>
          <button onClick={() => dispatch({ type: "ADD" })}>
            add use dispatch
          </button>
          <button onClick={add}>add</button>
          <button onClick={minus}>minus</button>
          <button onClick={this.props.aadd}>1秒后+1</button>
        </div>
      );
    }
  }
);

2、实现react-redux

编写Xreact-redux.js ,实现connect、Provider、bindActionCreator

import React, { Component } from "react";

// 传递store
const ValueContext = React.createContext();

// connect
export const connect = (
  mapStateToProps = (state) => state,
  mapDispatchToProps
) => (WrappedComponent) => {
  return class extends Component {
    static contextType = ValueContext;
    constructor(props) {
      super(props);
      this.state = {
        props: {},
      };
    }
    componentDidMount() {
      this.update();
      // 拿到store的订阅函数
      const { subscribe } = this.context;
      subscribe(() => {
        this.update();
      });
    }
    update = () => {
      const { getState, dispatch } = this.context;
      /*(state) => ({ count: state })*/
      const stateProps = mapStateToProps(getState());
      let dispatchProps;
      console.log("mapDispatchToProps", mapDispatchToProps); //sy-log
      // 当dispatch是对象时,需要绑定dispatch
      // { add: () => ({type: "ADD"}) }
      if (typeof mapDispatchToProps === "object") {
        dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
      } else if (typeof mapDispatchToProps === "function") {
        // 当dispatch是函数时
        /**
         (dispatch) => {
          let res = {
            add: () => ({ type: "ADD" }),
            minus: () => ({ type: "MINUS" }),
          };
          res = bindActionCreators(res, dispatch);
          return { ...res };
         }
         */
        dispatchProps = mapDispatchToProps(dispatch, this.props);
      } else {
        dispatchProps = { dispatch };
      }
      this.setState({
        props: {
          ...stateProps,
          ...dispatchProps,
        },
      });
    };
    render() {
      return <WrappedComponent {...this.props} {...this.state.props} />;
    }
  };
};

// Provider
// /context
export class Provider extends Component {
  render() {
    return (
      <ValueContext.Provider value={this.props.store}>
        {this.props.children}
      </ValueContext.Provider>
    );
  }
}

// let creators = {
//     add: () => ({type: "ADD"}),
//     minus: () => ({type: "MINUS"})
// };
function bindActionCreator(creator, dispatch) {
  return (...args) => dispatch(creator(...args));
}

export function bindActionCreators(creators, dispatch) {
  const obj = {};
  for (const key in creators) {
    obj[key] = bindActionCreator(creators[key], dispatch);
  }
  return obj;
}

3、react-router简介

react-router包含3个库,react-router、react-router-dom和 react-router-native。

react-router提供最基本的路由功能, 实际使用的时候我们不会直接安装react-router,而是根据应用运行的环境选择安装react-router-dom(在浏览器中使用)或react-router-native(在rn中使用)。

react-router-dom和react-router-native都依赖react-router,所以在安装时,react-router也会自动安装,创建web应用。

安装

npm install --save react-router-dom

基本使用

react-router中奉行一切皆组件的思想,路由器-Router、链接-Link、路由-Route、独占-Switch、重定向-Redirect都以组件形式存在

创建RouterPage.js

import React, { Component } from "react";
import { BrowserRouter, Link, Route } from "react-router-dom";
import HomePage from "./HomePage";
import UserPage from "./UserPage";
export default class RouterPage extends Component {
  render() {
    return (
      <div>
        <h1>RouterPage</h1>
        <BrowserRouter>
          <nav>
            <Link to="/">首页</Link>
            <Link to="/user">用户中心</Link>
          </nav>
          {/* 根路由要添加exact,实现精确匹配 */}
          <Route exact path="/" component={HomePage} />
          <Route path="/user" component={UserPage} />
        </BrowserRouter>
      </div>
    );
  }
}

Route渲染内容的三种方式

Route渲染优先级:children>component>render。

三者能接收到同样的[route props],包括match, location and history。

这三种方式互斥,你只能用一种 。

children:函数

有时候,不管location是否匹配,你都需要渲染一些内容,这时候你可以用children。

除了不管location是否匹配都会被渲染之外,其它工作方法与 render完全一样。

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";
function ListItemLink({ to, name, ...rest }) {
  return (
    <Route
      path={to}
      children={({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to} {...rest}>
            {name}
          </Link>
        </li>
      )}
    />
  );
}
export default class RouteChildren extends Component {
  render() {
    return (
      <div>
        <h3>RouteChildren</h3>
        <Router>
          <ul>
            <ListItemLink to="/somewhere" name="链接1" />
            <ListItemLink to="/somewhere-else" name="链接2" />
          </ul>
        </Router>
      </div>
    );
  }
}

render:函数

但是当你用render的时候,你调用的只是个函数。但是它和 component一样,能访问到所有的[route props]。

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
// 方便的内联渲染
ReactDOM.render(
  <Router>
    <Route path="/home" render={() => <div>Home</div>} />
  </Router>,
  node
);

// wrapping/composing
//把route参数传递给你的组件
function FadingRoute({ component: Component, ...rest }) {
  return (
    <Route {...rest} render={(routeProps) => <Component {...routeProps} />} />
  );
}
ReactDOM.render(
  <Router>
    <FadingRoute path="/cool" component={Something} />
  </Router>,
  node
);

component: component

只在当location匹配的时候渲染。

import React, { Component, useEffect } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
export default class RouteComponePage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }
  render() {
    const { count } = this.state;
    return (
      <div>
        <h3>RouteComponePage</h3>
        <button onClick={() => { this.setState({ count: count + 1 }); }}>
          click change count {count}
        </button>
        <Router>
          {/* 渲染component的时候会调用
          React.createElement,如果使用下面这种匿名函数的形
          式,每次都会生成一个新的匿名的函数,
          导致生成的组件的type总是不相同,这个时候会
          产生重复的卸载和挂载 */}

          {/* 错误举例 观察下child的didMount和willUnmount函数,会一直挂载卸载 */}
          {/* <Route component={() => <Child count={count} />} /> */}
          {/* <Route component={() => <FunctionChild count={count} />} /> */}

          {/* 下面才是正确的示范 */}
          {/* <Route render={() => <Child count={count} />} /> */}
          <Route render={() => <FunctionChild count={count} />} />

          {/* children 呢 */}
          {/* <Route children={() => <Child count={count} />} /> */}
          <Route children={() => <FunctionChild count={count} />} />
        </Router>
      </div>
    );
  }
}
class Child extends Component {
  componentDidMount() {
    console.log("componentDidMount"); //sy-log
  }
  componentWillUnmount() {
    console.log("componentWillUnmount"); //sylog
  }
  render() {
    return <div>child-{this.props.count}</div>;
  }
}
// hook的例子
function FunctionChild(props) {
  useEffect(() => {
    return () => {
      console.log("WillUnmount"); //sy-log
    };
  }, []);
  return <div>child-{props.count}</div>;
}

注意

当你用component的时候,Router会用你指定的组件和 React.createElement创建一个新的[React element]。这意味着当你提供的是一个内联函数的时候,每次render都会创建一个新的组件。这会导致不再更新已经现有组件,而是直接卸载然后再去挂载一个新的组件。因此,当用到内联函数的内联渲染时,请使用render或者children。

使用Router

动态路由

使用:id的形式定义动态路由

<Route path="/search/:id" component={Search} />

添加导航链接:

<Link to={"/search/" + searchId}>搜索</Link>

创建Search组件并获取参数

import React, { Component } from "react";
import { BrowserRouter, Link, Route } from "react-router-dom";
import HomePage from "./HomePage";
import UserPage from "./UserPage";
function Search({ match, history, location }) {
  const { id } = match.params;
  return (
    <div>
      <h1>Search: {id}</h1>
    </div>
  );
}
export default class RouterPage extends Component {
  render() {
    const searchId = "1234";
    return (
      <div>
        <h1>RouterPage</h1>
        <BrowserRouter>
          <nav>
            <Link to="/">首页</Link>
            <Link to="/user">用户中心</Link>
            <Link to={"/search/" + searchId}>搜 索</Link>
          </nav>
          {/* 根路由要添加exact,实现精确匹配 */}
          <Route exact path="/" component={HomePage} />
          <Route path="/user" component={UserPage} />
          <Route path="/search/:id" component={SearchComponent} />
        </BrowserRouter>
      </div>
    );
  }
}

嵌套路由

Route组件嵌套在其他页面组件中就产生了嵌套关系

修改Search,添加新增和详情

function DetailComponent(props) {
  return <div>DetailComponent</div>;
}
function SearchComponent(props) {
  console.log("props", props); //sy-log
  const { id } = props.match.params;
  return (
    <div>
      <h3>SearchComponent</h3>
      <p>{id}</p>
      <Link to={"/search/" + id + "/detail"}>详 情</Link>
      <Route path="/search/:id/detail" component={DetailComponent} />
    </div>
  );
}

404页面

设定一个没有path的路由在路由列表最后面,表示一定匹配 。

{/* 添加Switch表示仅匹配一个*/}
<Switch>
  {/* 根路由要添加exact,实现精确匹配 */}
  <Route exact path="/" component={HomePage} />
  <Route path="/user" component={UserPage} />
  <Route path="/search/:id" component={Search} />
  <Route render={() => <h1>404</h1>} />
</Switch>;

路由守卫

思路:创建高阶组件包装Route使其具有权限判断功能

创建PrivateRoute

import React, { Component } from "react";
import { Route, Redirect } from "react-routerdom";
export default class PrivateRoute extends Component {
  render() {
    const { isLogin, path, component } = this.props;
    if (isLogin) {
      // 登录
      return <Route path={path} component={component} />;
    } else {
      // 去登录,跳转登录页面
      return (
        <Redirect to={{ pathname: "/login", state: { redirect: path } }} />
      );
    }
  }
}

创建LoginPage.js

import React, { Component } from "react";
import { Redirect } from "react-router-dom";
export default class LoginPage extends Component {
  render() {
    const { isLogin, location } = this.props;
    const { redirect = "/" } = location.state || {};
    console.log("props", this.props); //sy-log
    if (isLogin) {
      // 已经登录
      return <Redirect to={redirect} />;
    } else {
      return (
        <div>
          <h3>LoginPage</h3>
          <button onClick={() => {}}>click</button>
        </div>
      );
    }
  }
}

在RouterPage.js配置路由,RouterPage ,访问其他页面先进入PrivateRoute

<Route exact path="/login" component={LoginPage} />
<PrivateRoute path="/user" component={UserPage}/>

评论(0)


最新评论

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis