06-React 脚手架

97 阅读20分钟

React 脚手架

01-脚手架自带文件

可以避免同名 css 被覆盖的问题,后引入的组件样式会覆盖同名的前面的样式

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import Hello from "./components/Hello";
import Welcome from "./components/Welcome";
export default class App extends Component {
  render() {
    return (
      <div>
        <Hello />
        <Welcome />
      </div>
    );
  }
}

Hello.jsx

import React, { Component } from "react";
import hello from "./index.module.css";
export default class Hello extends Component {
  render() {
    return (
      <div className={hello.hello}>
        <h2>Hello</h2>
      </div>
    );
  }
}

Welcome.jsx

import React, { Component } from "react";
import "./index.css";
export default class Welcome extends Component {
  render() {
    return (
      <div className="welcome">
        <h1>Welcome</h1>
      </div>
    );
  }
}

03-配置代理 setupProxy

setupProxy.js

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

module.exports = function (app) {
  app.use(
    proxy("/api1", {
      // 遇见api1前缀的请求就会触发该代理配置
      targer: "http://localhost:5000", // 请求转发给谁
      changeOrigin: true, // 控制服务器收到的响应头重host字段的值
      /*
      	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: { "^/api1": "" }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    })
  );
  app.use(
    proxy("/api2", {
      targer: "http://localhost:5001",
      changeOrigin: true,
      pathRewrite: { "^/api2": "" },
    })
  );
};

04-消息订阅与发布

Hello.jsx

import React, { Component } from "react";
import PubSub from "pubsub-js";

export default class Hello extends Component {
  talkWecome = () => {
    PubSub.publish("talkWecome", { info: "hello发送数据给welcome" });
  };
  render() {
    return (
      <div>
        <h2>Hello</h2>
        <button onClick={this.talkWecome}>告诉welcome</button>
      </div>
    );
  }
}

Welcome.jsx

import React, { Component } from "react";
import PubSub from "pubsub-js";
export default class Welcome extends Component {
  state = { info: "" };
  componentDidMount() {
    this.token = PubSub.subscribe("talkWecome", (_, data) => {
      this.setState(data);
    });
  }
  componentWillUnmount() {
    PubSub.unsubscribe(this.token);
  }
  render() {
    return (
      <div className="welcome">
        <h1>Welcome</h1>
        <h2>{this.state.info}</h2>
      </div>
    );
  }
}

05-路由的基本使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

ReactDOM.render(
  // 一个项目只允许有一个 BrowserRouter
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <Link to="/about"> About </Link>
        <Link to="/home"> Home </Link>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
      </div>
    );
  }
}

About.jsx

import React, { Component } from "react";
export default class About extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px", color: "red" }}>
              About
            </a>
            <a>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>About</h2>
        </div>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px" }}>About</a>
            <a style={{ color: "red" }}>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>Home</h2>
        </div>
      </div>
    );
  }
}

06-NavLink 的使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

ReactDOM.render(
  // 一个项目只允许有一个 BrowserRouter
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, BrowserRouter, Route } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import Hello from "./components/Hello";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <NavLink activeClassName="demo" to="/about">
          About
        </NavLink>
        <NavLink activeClassName="demo" to="/home">
          Home
        </NavLink>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

About.jsx

import React, { Component } from "react";
export default class About extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px", color: "red" }}>
              About
            </a>
            <a>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>About</h2>
        </div>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px" }}>About</a>
            <a style={{ color: "red" }}>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>Home</h2>
        </div>
      </div>
    );
  }
}

07-封装 MyNavLink

03-React-cli\07-封装 MyNavLink\components\MyNavLink\index.jsx

import React, { Component } from "react";
import { NavLink } from "react-router-dom";

export default class MyNavLink extends Component {
  render() {
    console.log(this.props);
    return (
      <div>
        <NavLink {...this.props} activeClassName="demo" to={this.props.to} />
      </div>
    );
  }
}

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, BrowserRouter, Route } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import Hello from "./components/Hello";
import MyNavLink from "./components/MyNavLink";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        {/* 接收标签体内容 */}
        {/* 标签体内容也是一个特殊的标签属性 在props里面的 children */}
        <MyNavLink to="/about">about</MyNavLink>
        <MyNavLink to="/home">home</MyNavLink>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

08-Switch 的使用

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <Link to="/about"> About </Link>
        <Link to="/home"> Home </Link>
        {/* 注册路由 */}
        {/* 这个组件只会让路由注册一个,匹配到后下面的组件就不展示了 */}
        <Switch>
          <Route path="/about" component={About} />
          <Route path="/home" component={Home} />
          <Route path="/home" component={Test} />
        </Switch>
      </div>
    );
  }
}

09-解决样式丢失问题

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, HashRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 这样写法会出现样式问题,因为 /LGQ/ 会被浏览器当做路径去处理 */}
        {/* 解决方案 */}
        {/* 1. <link href="/css/bootstrap.css" rel="stylesheet">  */}
        {/* 2. <link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet">  */}
        {/* 3. 修改 BrowserRouter 为 HashRouter */}
        <MyNavLink to="/LGQ/about">About</MyNavLink>
        <MyNavLink to="/LGQ/home">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route path="/LGQ/about" component={About} />
          <Route path="/LGQ/home" component={Home} />
          <Route path="/LGQ/home" component={Test} />
        </Switch>
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

10-模糊匹配与精准匹配

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, HashRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 模糊匹配路由: Route 要的 MyNavLink 必须一个不能少,但是可以多 */}
        {/* to="/home/a/b" path="/home" 可以 */}
        {/* to="/home/" path="/homea/b" 不可以 */}

        {/* 严格匹配路由: exact */}
        <MyNavLink to="/about">About</MyNavLink>
        <MyNavLink to="/home/a/b">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route exact path="/about" component={About} />
          <Route exact path="/home" component={Home} />
          <Route exact path="/test" component={Test} />
        </Switch>
      </div>
    );
  }
}

11-嵌套路由的使用

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Route, Redirect } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        <MyNavLink to="/about">About</MyNavLink>
        <MyNavLink to="/home">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route path="/about" component={About} />
          <Route path="/home" component={Home} />
          <Redirect to="/about"></Redirect>
        </Switch>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
import MyNavLink from "../../components/MyNavLink";
import { Route } from "react-router-dom";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";

import Message from "./Message";
import News from "./News";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <MyNavLink to="/home/message">Message</MyNavLink>
        <MyNavLink to="/home/news">News</MyNavLink>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h4>展示区</h4>
          <h4>Home</h4>
          <Switch>
            <Route path="/home/message" component={Message} />
            <Route path="/home/news" component={News} />
          </Switch>
        </div>
      </div>
    );
  }
}

Message.jsx

import React, { Component } from "react";

export default class Message extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>Message 1</li>
          <li>Message 2</li>
          <li>Message 3</li>
          <li>Message 4</li>
        </ul>
      </div>
    );
  }
}

News.jsx

import React, { Component } from "react";

export default class News extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>News 1</li>
          <li>News 2</li>
          <li>News 3</li>
          <li>News 3</li>
        </ul>
      </div>
    );
  }
}

12-路由组件传递 params 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        <Route path="/home/message/detail/:id/:title" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 Params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    return (
      <div>
        <p>ID: {this.props.match.params.id}</p>
        <p>Title: {this.props.match.params.title}</p>
        <p>Content: {data[this.props.match.params.id]}</p>
      </div>
    );
  }
}

13-路由组件传递 search 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              {/* <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link> */}

              {/* 2. Link 携带 search 参数 */}
              <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    const { id, title } = qs.parse(this.props.location.search.slice(1));
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

14-路由组件传递 state 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              {/* <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link> */}

              {/* 2. Link 携带 search 参数 */}
              {/* <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link> */}

              {/* 3. Link 携带 state 参数 */}
              <Link
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        {/* <Route path="/home/message/detail" component={Detail} /> */}

        {/* 3. state 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    // const { id, title } = qs.parse(this.props.location.search.slice(1));
    // return (
    //   <div>
    //     <p>ID: {id}</p>
    //     <p>Title: {title}</p>
    //     <p>Content: {data[id]}</p>
    //   </div>
    // );

    // 3. 接受 state 参数
    const { id, title } = this.props.location.state;
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

15-路由的 replace 模式

messages.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 3. Link 携带 state 参数 */}
              {/* replace模式就不会留下记录 */}
              <Link
                replace
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
      </div>
    );
  }
}

16-编程式路由导航

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  pushShow = (el) => {
    // push 跳转携带 params 参数
    // this.props.history.push(`/home/message/detail/${el.id}/${el.title}`);

    // push 跳转携带 search 参数
    // this.props.history.push(
    //   `/home/message/detail/?id=${el.id}&title=${el.title}`
    // );

    // push 跳转携带 state 参数
    this.props.history.push("/home/message/detail", {
      id: el.id,
      title: el.title,
    });
  };
  replaceShow = (el) => {
    // replace 跳转携带params参数
    // this.props.history.replace(`/home/message/detail/${el.id}/${el.title}`);

    // replace 跳转携带 search 参数
    // this.props.history.replace(
    //   `/home/message/detail/?id=${el.id}&title=${el.title}`
    // );

    // replace 跳转携带 state 参数
    this.props.history.replace("/home/message/detail", {
      id: el.id,
      title: el.title,
    });
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link>
              <button onClick={() => this.pushShow(el)}>push查看</button>
              <button onClick={() => this.replaceShow(el)}>replace查看</button>
              {/* 2. Link 携带 search 参数 */}
              {/* <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link> */}

              {/* 3. Link 携带 state 参数 */}
              {/* <Link
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link> */}
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        {/* <Route path="/home/message/detail" component={Detail} /> */}

        {/* 3. state 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    // const { id, title } = qs.parse(this.props.location.search.slice(1));
    // return (
    //   <div>
    //     <p>ID: {id}</p>
    //     <p>Title: {title}</p>
    //     <p>Content: {data[id]}</p>
    //   </div>
    // );

    // 3. 接受 state 参数
    const { id, title } = this.props.location.state;
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

17-withRouter 的使用

Header.jsx

import React, { Component } from "react";
import { withRouter } from "react-router-dom/cjs/react-router-dom.min";
class Header extends Component {
  back = () => {
    this.props.history.goBack();
  };
  forward = () => {
    this.props.history.goForward();
  };
  go = () => {
    this.props.history.back();
  };
  render() {
    console.log(this.props);
    return (
      <div>
        <h1>我是APP</h1>
        <button onClick={this.back}>回退</button>
        <button onClick={this.forward}>前进</button>
        <button onClick={this.go}>go</button>
      </div>
    );
  }
}
// withRouter 可以把一般组件加工成路由组件,让一般组件的props有路由组件的API
// withRouter的返回值是一个新组件

export default withRouter(Header);

18-求和 React 版本

Count.jsx

import React, { Component } from "react";

export default class Count extends Component {
  state = { count: 0 };
  increment = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    this.setState({
      count,
    });
  };
  decrement = () => {
    const { value } = this.selectNumber;
    const count = this.state.count - Number(value);
    this.setState({
      count,
    });
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    if (this.state.count % 2 !== 0) {
      this.setState({
        count,
      });
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    setTimeout(() => {
      this.setState({
        count,
      });
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {this.state.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

19-Redux 精简版

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
console.log(store);
export default class Count extends Component {
  // componentDidMount() {
  //   // 检测redux中数据的变化,变化就调用render
  //   store.subscribe(() => {
  //     this.setState({});
  //   });
  // }
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "increment", data: value - 0 });
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "decrement", data: value - 0 });
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch({ type: "increment", data: value - 0 });
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    setTimeout(() => {
      store.dispatch({ type: "increment", data: value - 0 });
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

03-React-cli\19-Redux 精简版\redux\count_reducer.js

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  //   console.log(type, data);
  switch (type) {
    case "increment":
      return preState + data;
    case "decrement":
      return preState - data;
    default:
      return preState;
  }
}

03-React-cli\19-Redux 精简版\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore } from "redux";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer);

20-Redux 完整版

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
import {
  createIncrementAction,
  createDecrementAction,
} from "../../redux/count_action";
console.log(store);
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value - 0));
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value - 0));
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch(createIncrementAction(value - 0));
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    setTimeout(() => {
      store.dispatch(createIncrementAction(value - 0));
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

redux\constant.js

// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";

redux\count_action.js

import { INCREMENT, DECREMENT } from "./constant";

// 该文件专门为count组件生成action对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });

redux\count_reducer.js

import { INCREMENT, DECREMENT } from "./constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  //   console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}

redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore } from "redux";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer);

21-Redux 异步

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value - 0));
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value - 0));
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch(createIncrementAction(value - 0));
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAsyncAction(value - 0, 500));
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer, applyMiddleware(thunk));

redux\count_action.js

import { INCREMENT, DECREMENT } from "./constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};

22-React-Redux 的基本使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下
store.subscribe(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById("root")
  );
});
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import CountUI from "./containers/Count";
import store from "./redux/store";
export default class App extends Component {
  render() {
    return (
      <div>
        <CountUI store={store} />
      </div>
    );
  }
}

03-React-cli\22-React-Redux 的基本使用\containers\Count\index.jsx

// 引入CountUI组件
import CountUI from "../../components/Count";

import { INCREMENT, DECREMENT } from "../../redux/constant";
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 引入 store ,这里的 store 不能自己去引入,要去上层使用 props 传过来

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

/**
 * 1. 因为 props 取的时候得 key value 的形式,所以需要对象的形式,
 * 2. 返回的对象作为对象会传给组件 - 状态
 * 3. a 函数的返回值作为状态返回给了UI组件
 */
function mapStateToProps(state) {
  // state 是他们传给我们,不需要自己引入
  return { count: state };
}
/**
 * 1. 因为 props 取的时候得 key value 的形式,所以需要对象的形式,
 * 2. 返回的对象作为对象会传给组件 - 操作状态的方法
 */
function mapDispatchToProps(dispatch) {
  // dispatch 也是他们传给我们的,不需要我们自己引入
  return {
    increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
    decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
    incrementAsync: (data, time) =>
      dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  };
}
// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

03-React-cli\22-React-Redux 的基本使用\components\Count

import React, { Component } from "react";
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和值为: {this.props.count}</h1>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下
store.subscribe(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById("root")
  );
});
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

23-react-redux 优化

03-React-cli\23-react-redux 优化\containers\Count

// 定义CountUI组件
import React, { Component } from "react";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和值为: {this.props.count}</h1>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({ count: state }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment: createIncrementAction,
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction,
  }
)(Count);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import Count from "./containers/Count";
export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
      </div>
    );
  }
}

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
import { Provider } from "react-redux";

// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下

// 但是,用上 react-redux 之后就不需要这个了
// store.subscribe(() => {
//   ReactDOM.render(
//     <BrowserRouter>
//       <App />
//     </BrowserRouter>,
//     document.getElementById("root")
//   );
// });

ReactDOM.render(
  // 在这里这样写的话就不需要每个组件都传 store 了
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

24-react-redux 组件间数据共享

03-React-cli\24-react-redux 组件间数据共享\containers\Count\index.jsx

// 定义CountUI组件
import React, { Component } from "react";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/actions/count";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h4>我是 Count 组件,下方组件人数为{this.props.person.length}</h4>
        <h5>当前求和值为: {this.props.count}</h5>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({ person: state.person, count: state.count }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment: createIncrementAction,
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction,
  }
)(Count);

03-React-cli\24-react-redux 组件间数据共享\containers\Person\index.jsx

import React, { Component } from "react";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

import { createAddPersonAction } from "../../redux/actions/person";
class Person extends Component {
  AddPerson = () => {
    const name = this.nameNode.value;
    const age = this.ageNode.value;
    this.props.AddPerson({ name, age });
    this.nameNode.value = this.ageNode.value = "";
  };
  render() {
    return (
      <div>
        <h4>我是 Person 组件,上方组件求和为{this.props.count}</h4>
        <input
          ref={(c) => (this.nameNode = c)}
          type="text"
          placeholder="输入名字"
        />
        <input
          ref={(c) => (this.ageNode = c)}
          type="text"
          placeholder="输入年龄"
        />
        <button onClick={this.AddPerson}>添加</button>
        <ul>
          {this.props.person.map((el) => {
            return (
              <li key={el.id}>
                名字{el.name},年龄{el.age}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default connect(
  (state) => ({ person: state.person, count: state.count }),
  {
    AddPerson: createAddPersonAction,
  }
)(Person);

redux

actions
// 03-React-cli\24-react-redux组件间数据共享\redux\actions\count.js
import { INCREMENT, DECREMENT } from "../constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};

// 03-React-cli\24-react-redux组件间数据共享\redux\actions\person.js
import { ADD_PERSON } from "../constant";

export const createAddPersonAction = (personObj) => ({
  type: ADD_PERSON,
  data: personObj,
});
reducers
// 03-React-cli\24-react-redux组件间数据共享\redux\reducers\count.js
import { INCREMENT, DECREMENT } from "../constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  // console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}


// 03-React-cli\24-react-redux组件间数据共享\redux\reducers\person.js

import { ADD_PERSON } from "../constant";

// 初始化人的列表
const initState = [];

export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  switch (type) {
    case ADD_PERSON:
      return [{ ...data, id: new Date().getTime() }, ...preState];
    // return initState.push({ ...data, id: new Date().getTime() });
    default:
      return preState;
  }
}

03-React-cli\24-react-redux 组件间数据共享\redux\constant.js
// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON = "add_person";
03-React-cli\24-react-redux 组件间数据共享\redux\store.js
// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./reducers/count";
// 引入为 person 组件服务的 reducer
import personReducer from "./reducers/person";
// 合并 reducers
const allReduces = combineReducers({
  count: countReducer,
  person: personReducer,
});
export default createStore(allReduces, applyMiddleware(thunk));

25-react-redux 开发者工具

03-React-cli\25-react-redux 开发者工具\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./reducers/count";
// 引入为 person 组件服务的 reducer
import personReducer from "./reducers/person";

// 引入 redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";

// 合并 reducers
const allReduces = combineReducers({
  count: countReducer,
  person: personReducer,
});
export default createStore(
  allReduces,
  composeWithDevTools(applyMiddleware(thunk))
);

26-react-redux 最终版

03-React-cli\26-react-redux 最终版\containers

// 03-React-cli\26-react-redux最终版\containers\Count\index.jsx

// 定义CountUI组件
import React, { Component } from "react";

import {
  increment,
  decrement,
  incrementAsync,
} from "../../redux/actions/count";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h4>我是 Count 组件,下方组件人数为{this.props.persons.length}</h4>
        <h5>当前求和值为: {this.props.count}</h5>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({
    persons: state.persons,
    count: state.count
  }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment,
    decrement,
    incrementAsync,
  }
)(Count);



// 03-React-cli\26-react-redux最终版\containers\Person\index.jsx
import React, { Component } from "react";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

import { AddPerson } from "../../redux/actions/person";
class Person extends Component {
  AddPerson = () => {
    const name = this.nameNode.value;
    const age = this.ageNode.value;
    this.props.AddPerson({ name, age });
    this.nameNode.value = this.ageNode.value = "";
  };
  render() {
    return (
      <div>
        <h4>我是 Person 组件,上方组件求和为{this.props.count}</h4>
        <input
          ref={(c) => (this.nameNode = c)}
          type="text"
          placeholder="输入名字"
        />
        <input
          ref={(c) => (this.ageNode = c)}
          type="text"
          placeholder="输入年龄"
        />
        <button onClick={this.AddPerson}>添加</button>
        <ul>
          {this.props.persons.map((el) => {
            return (
              <li key={el.id}>
                名字{el.name},年龄{el.age}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default connect(
  (state) => ({
    persons: state.persons,
    count: state.count,
  }),
  {
    AddPerson,
  }
)(Person);

03-React-cli\26-react-redux 最终版\redux\actions

// 03-React-cli\26-react-redux最终版\redux\actions\count.js

import { INCREMENT, DECREMENT } from "../constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const increment = (data) => ({ type: INCREMENT, data });
export const decrement = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const incrementAsync = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment(data));
    }, time);
  };
};

// 03-React-cli\26-react-redux最终版\redux\actions\person.js
import { ADD_PERSON } from "../constant";

export const AddPerson = (personObj) => ({
  type: ADD_PERSON,
  data: personObj,
});

03-React-cli\26-react-redux 最终版\redux\reducers

// 03-React-cli\26-react-redux最终版\redux\reducers\count.js

import { INCREMENT, DECREMENT } from "../constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  // console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}



// 03-React-cli\26-react-redux最终版\redux\reducers\count.js
import { ADD_PERSON } from "../constant";
// 初始化人的列表
const initState = [];
export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  // 在这里return 的时候,如果返回同一个数据,那么redux是不去更新页面的
  switch (type) {
    case ADD_PERSON:
      return [{ ...data, id: new Date().getTime() }, ...preState];
    default:
      return preState;
  }
}






// 03-React-cli\26-react-redux最终版\redux\reducers\index.js
// 该文件用于汇总所有的 reduce
// 引入createStore,专门用于创建store对象
import { combineReducers } from "redux";
// 引入为 count 组件服务的 reducer
import count from "./count";
// 引入为 person 组件服务的 reducer
import persons from "./person";

// 合并 reducers
export default combineReducers({
  count,
  persons,
});





03-React-cli\26-react-redux 最终版\redux\constant.js

// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON = "add_person";

03-React-cli\26-react-redux 最终版\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入 redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";
// 引入所有的 reducers
import allReduces from "./reducers/index";

export default createStore(
  allReduces,
  composeWithDevTools(applyMiddleware(thunk))
);
03-React-cli\26-react-redux 最终版\App.jsx
//创建外壳组件APP
import React, { Component } from "react";
import Count from "./containers/Count";
import Person from "./containers/Person";
export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
        <hr />
        <Person />
      </div>
    );
  }
}

03-React-cli\26-react-redux 最终版\index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
import { Provider } from "react-redux";

// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下

// 但是,用上 react-redux 之后就不需要这个了
// store.subscribe(() => {
//   ReactDOM.render(
//     <BrowserRouter>
//       <App />
//     </BrowserRouter>,
//     document.getElementById("root")
//   );
// });

ReactDOM.render(
  // 在这里这样写的话就不需要每个组件都传 store 了
  // 此处需要 Provider 包裹一下 App,目的是为了 App 后代都可以接收到 store
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);