• react+redux+hooks


    2019年5月24日 开始对于react的学习

    http://huziketang.mangojuice.top/books/react/lesson46

    开始提要:reactjs是不依赖nodejs的,但是webpack是依赖的,所以我们react开发也要安装node环境!
    安装node的教程我要另开一个笔记,就在自己的博客园里面了:

    react和小程序以及vue都是不一样的,他完全改变了我对隔离霜的看法【口区】

    废话不啰嗦,现在开始!

    首先,惯例介绍我的参考文献:https://react.docschina.org/docs/introducing-jsx.html

    1、hello.world JSX
    react的语法不同,像是xml语法,又像是js语法,本身就是js语法的一种扩展,所以他用jsx语法
    JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析
    我们想要创建一个react hello实例的话,就像01demo.html
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    ReactDOM.render(
    <h1>Hello World!</h1>,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这里要注意,我们的js要引入完,因为不是直接用cnpm引入的,所以三个都要引入
    以及,他的script,type不再是:text/javascript 而是text/babel 不然也是不对的

    用react创建新dom的时候,属性要用驼峰式,比如class变成className

    2、元素渲染
    元素描述了你在屏幕上想看到的内容
    const element = <h1>hello world!</h1>
    比如上面这个,hello world就是他想渲染的

    我们在写01demo的时候,注意到,div里面有一个id=root的,这就是根节点,因为该节点内的所有内容都将由react dom管理
    仅仅使用react构建的应用通常只有单一的根DOM节点,当然也可以有多个独立的,不过这里先不说了

    想要将一个react元素渲染到根root节点中,只需把它们一起传入reactDOM.render():
    const element = <h1>hello world!</h1>
    ReactDOM.render(element,document.getElementById('root'));

    我们这里再来一个02demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
    #root{
    color: #f66;
    }
    </style>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    const element = <h1>张卫健,你好啊~</h1>
    ReactDOM.render(
    element,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这个例子说明了我们的猜想,并且!说明了css是可以直接写哒~

    基本上react元素是不可变对象,一旦被创建,就无法再次更改他的子元素或者属性。所以我们想要更新这个元素的话,就必须重新创建一个新的元素,并且将其传入reactDOM。render()
    而且这也不是一劳永逸的,比如只想换里面的一部分元素,就没办法了,先看03demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function tick (){
    const element = (
    <div>
    <h1>这里来个定时器</h1>
    <h2>TIME: {new Date().toLocaleTimeString()}.</h2>
    </div>
    )
    ReactDOM.render(
    element,
    document.getElementById('root')
    )
    }
    setInterval(tick,1000);
    </script>
    </body>
    </html>
    就想到于,每一秒重新更新一次root里面的内容 但是react有一个优点,就是只更新他比较出来不一样的那一部分!
    换句话说,react dom元素会和他的子元素之间的状态进行比较,进行必要的更换,这点可以通过浏览器的检查工具看到:
    <div id="root">
    <div>
    <h1>这里来个定时器</h1>
    <h2>TIME: 下午4:42:49.</h2> //只有这里一直闪,背景为紫色,说明只有这里被重新渲染了
    </div>
    </div>

    3、组件&props
    组件允许你将UI拆分为独立可复用的代码片段,并对每个片段进行独立构思。
    组件,从概念上类似于JavaScript函数。他接受任意的入参(就是props),并返回用于描述页面展示内容的react元素

    3.1、函数组件与class组件
    3.1.1、定义组件最简单的方式就是编写JavaScript函数:
    function Welcome(prop){
    return <h1>hello , {props.name}</h1>
    }

    3.1.2、用es6的class定义组件
    class welcome extends React.Component{
    render(){
    return <h1>hello , {this.props.name}</h1>
    }
    }

    用class定义组件,涉及到props定义的问题,所以在学习class的其他属性前,我们用函数定义组件。

    3.2、渲染组件
    我们之前一直用dom标签,其实react元素也可以是自定义组件,我们看04demo

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function Welcome (props){
    return <h1>hello,{props.name}</h1>

    }
    const element = <Welcome name="张卫健" />;
    ReactDOM.render(
    element,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这里要记得,函数名一定要是【大写】的!必须是!!!
    并且,element不单单可以等于一个元素,也可以等于多个,比如05demo,这里就不复制了,主要是为了下面组件组合:

    3.3、组合组件
    组件可以再引入其他组件,我们用函数组件的方法,来个06demo

    3.4、提取组件
    就是把组件拆分成更小的组件
    function Comment(props) {
    return (
    <div className="Comment">
    <div className="UserInfo">
    <img className="Avatar"
    src={props.author.avatarUrl}
    alt={props.author.name}
    />
    <div className="UserInfo-name">
    {props.author.name}
    </div>
    </div>
    <div className="Comment-text">
    {props.text}
    </div>
    <div className="Comment-date">
    {formatDate(props.date)}
    </div>
    </div>
    );
    }

    首先上面就是一个组件,但是她太繁琐了,不好维护,我们就要拆分,下面看07demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function formatDate(date) {
    return date.toLocaleDateString();
    }
    //先是avatar
    function Avatar(props){
    return (
    <img className="Avatar"
    src={props.user.avatarUrl}
    alt={props.user.name}
    />
    )
    }

    //然后userInfo
    function UserInfo(props){
    return (
    <div className="UserInfo">
    <Avatar user={props.user} />
    <div className="UserInfo-name">
    {props.user.name}
    </div>
    </div>
    )
    }

    function Comment(props) {
    return (
    <div className="Comment">
    <UserInfo user={props.author} />
    <div className="Comment-text">
    {props.text}
    </div>
    <div className="Comment-date">
    {formatDate(props.date)}
    </div>
    </div>
    );
    }

    const comment = {
    date: new Date(),
    text: 'I love dkzhang so much!',
    author: {
    name: 'Hello dk',
    avatarUrl: 'https://placekitten.com/g/64/64',
    },
    };

    ReactDOM.render(
    <Comment date={comment.date} text={comment.text} author={comment.author}/>,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这就完成了一个组件的拆分

    并且,我们要知道,props只有可读性,也就是说,他不会更改入参。但是学习了state之后,就可以更改,这就是后话了~

    今天的学习到此为止,明天就是周六了,下周继续呀!!!我总觉着下周过来会忘完怎么回事??? ----0524END

    4、state&生命周期
    我们在03demo写定时器的时候,发现这样一直全部更新状态比较鸡肋,这一节我们学习如何封装真正可复用的clock组件。
    首先我们先把定时器的外观封装:
    function Clock(props){
    return (
    <div>
    <h1>hello dk!</h1>
    <h2>It is {props.date.toLocaleTimeString().}</h2>
    </div>
    )
    }
    function tick(){
    ReactDOM.render(
    <Clock date = {new Date()} />,
    document.getElementById('root')
    );
    }
    setInterval(tick,1000);

    但是我们又不希望一直写<Clock date = {new Date()} />,这个时候,就要用到state了
    state与props相似,但是state是私有的,并且完全受控于当前组件。 而且state只能用在类组件里面,不能用于函数组件,所以要改写函数组件为【类组件】

    所以我们最后的修改如08demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class Clock extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    date:new Date()
    };
    }
    componentDidMount(){
    this.timeID = setInterval(
    ()=>this.tick(),
    1000
    );
    }
    componentWillUnmount(){
    clearInterval(this.timeID);
    }
    tick (){
    this.setState({
    date:new Date()
    });
    }
    render(){
    return(
    <div>
    <h1>这里来个定时器</h1>
    <h2>TIME: {this.state.date.toLocaleTimeString()}.</h2>
    </div>
    )
    }
    }
    ReactDOM.render(
    <Clock />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    要注意上面的两个周期函数,她代表了一个是【挂载】一个是【卸载】,当我们的组件已经被渲染到DOM中后,会运行componentDidMount方法,
    当我们的组件从DOM移除的时候,就会调用componentWillUnmount方法

    5、事件处理
    react事件的命名采用小驼峰式,而不是纯小写。
    使用jsx语法时你需要传入一个函数作为事件处理函数,也就是要用大括号包起来,如下:
    <button onClick={activateLasers}>
    Activate Lasers
    </button>

    React中另一个不同点是你不能通过返回false的方式阻止默认行为,你必须显式的使用preventDefault。如:
    function ActionLink(){
    function handleClick(e){
    e.preventDefault();
    console.log('The link was clicked.');
    }

    return(
    <a herf="#" onClick={handleClick}>
    Click me
    </a>
    );
    }

    在这里,e是一个合成事件。

    使用React时,你一般不需要使用addEventListener为已创建的DOM元素添加监听器。React恰恰与之相反,你仅需要在该元素初始渲染的时候添加一个监听器。
    当你使用类语法定义一个组件的时候,通常的做法是将事件处理函数声明为class中的方法。如09demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class Toggle extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    isToggleOn:true
    };
    this.handleClick = this.handleClick.bind(this);
    }
    handleClick(){
    this.setState(state => ({
    isToggleOn:!state.isToggleOn
    }));
    }
    render(){
    return(
    <button onClick={this.handleClick}>
    {this.state.isToggleOn?'ON':'OFF'}
    </button>
    )
    }
    }
    ReactDOM.render(
    <Toggle />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    6、条件渲染
    可以用if或者条件运算符去创建元素来表现当前的状态,然后让React根据他们来更新UI。
    如10demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function UserGreeting(props){
    return <h1>Welcome back!</h1>
    }
    function GuestGreeting(props){
    return <h1>Please sign up!</h1>
    }
    function Greeting(props){
    const isLoggedIn = props.isLoggedIn;
    if(isLoggedIn){
    return <UserGreeting />;
    }else{
    return <GuestGreeting />;
    }
    }
    ReactDOM.render(
    <Greeting isLoggedIn = {false} />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    再来一个比较高级的11demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function UserGreeting(props){
    return <h1>Welcome back!</h1>
    }
    function GuestGreeting(props){
    return <h1>Please sign up!</h1>
    }
    function Greeting(props){
    const isLoggedIn = props.isLoggedIn;
    if(isLoggedIn){
    return <UserGreeting />;
    }else{
    return <GuestGreeting />;
    }
    }

    function LoginButton(props){
    return(
    <button onClick={props.onClick}>
    logIn
    </button>
    )
    }
    function LogoutButton(props){
    return(
    <button onClick={props.onClick}>
    logOut
    </button>
    )
    }

    class LoginControl extends React.Component{
    constructor(props){
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {
    isLoggedIn:false
    }
    }
    handleLoginClick(){
    this.setState({
    isLoggedIn:true
    })
    }
    handleLogoutClick(){
    this.setState({
    isLoggedIn:false
    })
    }
    render(){
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if(isLoggedIn){
    button = <LogoutButton onClick={this.handleLogoutClick} />
    }else{
    button = <LoginButton onClick={this.handleLoginClick} />
    }
    return(
    <div>
    <Greeting isLoggedIn={isLoggedIn} />
    {button}
    </div>
    )
    }
    }
    ReactDOM.render(
    <LoginControl />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    6.1.与运算符&&
    通过花括号包裹代码,可以在jsx中嵌入任何表达式。因为在JavaScript中,true&&expression总是会返回expression,false&&expression总是会返回false,所以我们可以这样写12demo:
    function Milbox(props){
    const unreadMessages = props.unreadMessages;
    return(
    <div>
    <h1>Hello DK !</h1>
    {unreadMessages.length>0&&
    <h2>
    You have {unreadMessages.length} unread messages.
    </h2>}
    </div>
    )
    }
    const messages = ['React','liulu',"dkzhang"];
    ReactDOM.render(
    <Milbox unreadMessages={messages}/>,
    document.getElementById('root')
    )

    6.2.三目运算符
    这个比较简单,直接举一个例子
    render() {
    const isLoggedIn = this.state.isLoggedIn;
    return (
    <div>
    The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
    );
    }

    6.3.阻止组件渲染
    在极少数情况下,我们可能会希望组件隐藏,即使他已经被其他组件渲染,如果要完成这样的操作,我们可以让render方法直接返回null,而不进行任何渲染,如13demo:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function Warning(props){
    if(!props.warn){
    return null;
    }
    return(
    <div className="warning">
    Warning!
    </div>
    )
    }
    class Page extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    showWarning:true
    };
    this.handleToggleClick = this.handleToggleClick.bind(this);
    }

    handleToggleClick(){
    this.setState(state=>({
    showWarning:!state.showWarning
    }));
    }
    render(){
    return(
    <div>
    <Warning warn={this.state.showWarning} />
    <button onClick={this.handleToggleClick}>
    {this.state.showWarning ? 'Hide':'Show'}
    </button>
    </div>
    )
    }
    }
    ReactDOM.render(
    <Page />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    这里的重点在于if(!props.warn){return null;},如果我们进入这里的话,后面的就不会再渲染了。否则 ,执行下面的。

    7、列表&key
    这个没什么好说的,通过14demo直接看效果:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    const array = [1,2,3,4,5];
    const newArray = array.map((num)=>
    <li>{num}</li>
    )
    ReactDOM.render(
    <ul>{newArray}</ul>,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    我们在15demo中,将这个demo变成一个组件
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function SetArray(props){
    const numbers = props.numbers;
    const setItems = numbers.map((num)=>
    <li>{num*2}</li>
    );
    return(
    <ul>{setItems}</ul>
    )
    }
    const numbers = [2,3,4,5,6]
    ReactDOM.render(
    <SetArray numbers={numbers} />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    由上面的例子我们还可以看出,如果我们要更改numbers里面的数值,要在花括号{}里面进行计算,而不是写成{num}*2这样!!!

    但是,我们运行上面的例子的时候,会看到提示说:Warning: Each child in an array or iterator should have a unique "key" prop.
    这就是告诉我们要有一个key值,所以我们要给他加上:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function SetArray(props){
    const numbers = props.numbers;
    const setItems = numbers.map((num)=>
    <li key={num.toString()}>{num*2}</li>
    );
    return(
    <ul>{setItems}</ul>
    )
    }
    const numbers = [2,3,4,5,6]
    ReactDOM.render(
    <SetArray numbers={numbers} />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    这样就ok啦~!
    但是我们要注意,key值她不是随便写的,他只有放在就近的数组上下文中才有意义,比如下面16demo:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function SetLi(props){
    const number = props.num;
    return(
    <li>{number*2}</li>
    )
    }
    function SetArray(props){
    const numbers = props.numbers;
    const setItems = numbers.map((num)=>
    <SetLi key={num.toString()} num={num} />
    );
    return(
    <ul>{setItems}</ul>
    )
    }
    const numbers = [2,3,4,5,6]
    ReactDOM.render(
    <SetArray numbers={numbers} />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    在上面的例子中,我们把key放在了引入组件的位置,而不是放在里组件里面,li的属性里面,这就说明,我们的key只对自己所在的上下文是有意义的!否则是没有意义的!
    大量的经验告诉我们,凡是写在map()方法里面的,key写在里面是比较合适的~

    而且,key并非是全局唯一的,他只是说当两个兄弟元素之间传递的话,必须是唯一的!并且,key是传递给react,不传递给组件的,所以我们F12看元素的时候,是没有key的属性的,
    如果我们想要读到key的值,就把他赋值给其他显性属性!

    我们上面的例子,都是把map()方法得到的值赋值给一个变量,然后用花括号引入这个变量以渲染,实际上,我们可以直接用花括号里面写上表达式,省去赋值变量这一步,比如17demo:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function SetLi(props){
    const number = props.num;
    return(
    <li>{number*2}</li>
    )
    }
    function SetArray(props){
    const numbers = props.numbers;
    return(
    <ul>
    {
    numbers.map((num)=>
    <SetLi key={num.toString()} num={num} />
    )
    }
    </ul>
    )
    }
    const numbers = [2,3,4,5,6]
    ReactDOM.render(
    <SetArray numbers={numbers} />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>


    8、表单
    在React中,可变状态通常保存在组件的state属性中,并且只能通过使用setState()来更新
    所以我们可以把两者结合起来,使React的state成为“唯一数据源。渲染表单的React组件还控制着用户输入过程中表单发生的操作。
    被React以这种方式控制取值的表单输入元素就叫做“受控组件”。
    如18demo:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class NameForm extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    value:''
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange(e){
    this.setState({
    value:e.target.value
    })
    }
    handleSubmit(e){
    alert('提交的名字是:'+ this.state.value);
    e.preventDefault();
    }
    render(){
    return(
    <form onSubmit={this.handleSubmit}>
    <label>
    名字:
    <input type="text" onChange={this.handleChange} value={this.state.value} />
    </label>
    <input type="submit" value="提交" />
    </form>
    )
    }
    }
    ReactDOM.render(
    <NameForm />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    8.1.textarea也可以这样用:
    <script type="text/babel">
    class NameForm extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    namevalue:'',
    contvalue:'请输入你想输的玩楞'
    };
    this.handleChange1 = this.handleChange1.bind(this);
    this.handleChange2 = this.handleChange2.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange1(e){
    this.setState({
    namevalue:e.target.value
    })
    }
    handleChange2(e){
    this.setState({
    contvalue:e.target.value
    })
    }
    handleSubmit(e){
    console.log('提交的名字是:'+ this.state.namevalue);
    console.log('提交的内容是:'+ this.state.contvalue);
    e.preventDefault();
    }
    render(){
    return(
    <form onSubmit={this.handleSubmit}>
    <label>
    名字:
    <input type="text" onChange={this.handleChange1} value={this.state.namevalue} />
    </label>
    <label>
    内容:
    <textarea onChange={this.handleChange2} value={this.state.contvalue}></textarea>
    </label>
    <input type="submit" value="提交" />
    </form>
    )
    }
    }
    ReactDOM.render(
    <NameForm />,
    document.getElementById('root')
    )
    </script>

    8.2.select基本上也可以这么用
    <script type="text/babel">
    class NameForm extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    namevalue:'',
    contvalue:'请输入你想输的玩楞',
    fruitvalue:'coconut'
    };
    this.handleChange1 = this.handleChange1.bind(this);
    this.handleChange2 = this.handleChange2.bind(this);
    this.handleChange3 = this.handleChange3.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleChange1(e){
    this.setState({
    namevalue:e.target.value
    })
    }
    handleChange2(e){
    this.setState({
    contvalue:e.target.value
    })
    }
    handleChange3(e){
    this.setState({
    fruitvalue:e.target.value
    })
    }
    handleSubmit(e){
    console.log('提交的名字是:'+ this.state.namevalue);
    console.log('提交的内容是:'+ this.state.contvalue);
    console.log('提交的水果是:'+ this.state.fruitvalue);
    e.preventDefault();
    }
    render(){
    return(
    <form onSubmit={this.handleSubmit}>
    <label>
    名字:
    <input type="text" onChange={this.handleChange1} value={this.state.namevalue} />
    </label>
    <label>
    内容:
    <textarea onChange={this.handleChange2} value={this.state.contvalue}></textarea>
    </label>
    <label>
    <select onChange={this.handleChange3} value={this.state.fruitvalue}>
    <option value="grapefruit">葡萄柚</option>
    <option value="lime">柠檬</option>
    <option value="coconut">椰子</option>
    <option value="mango">芒果</option>
    </select>
    </label>
    <input type="submit" value="提交" />
    </form>
    )
    }
    }
    ReactDOM.render(
    <NameForm />,
    document.getElementById('root')
    )
    </script>

    8.3.文件input标签 <input type="file"/>
    因为他的value只读,所以它是React中的一个非受控组件。

    8.4.处理多个输入 直接上19demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class NameForm extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    persons:2,
    join:true,
    };
    this.handleChange = this.handleChange.bind(this);
    }
    handleChange(e){
    const target = e.target;
    const value = target.type==='checkbox'? target.checked : target.value;
    const name = target.name;
    //如果下面这个不写的话,是数字的时候,不管点击什么,都不会变化
    this.setState({
    [name]:value
    })
    }
    render(){
    return(
    <form>
    <label>
    参与:
    <input name='join' type="checkbox" onChange={this.handleChange} checked={this.state.join} />
    </label>
    <label>
    人数:
    <input name='persons' type="number" onChange={this.handleChange} value={this.state.persons} />
    </label>
    </form>
    )
    }
    }
    ReactDOM.render(
    <NameForm />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    上面的例子主要就是两点,意识给每一个input起一个name名字,这样到时候赋值的时候,就知道给谁了;第二就是要知道ES6里面计算属性名称的语法更新,也就是要把name包起来,就是【name】

    9、状态提升
    这是个大工程呀,其实就是说,当两个组件公用一个state时,我们把这个state提升到两个子组件的父组件里面,这样就比较好渲染啦!
    比如我们想做一个华氏度和摄氏度同步更新的功能,那就要用到状态提升了!
    如20demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    //首先做一个判断是否水沸
    function BoilingVerdict(props){
    if(props.celsius >= 100){
    return <p>The sater sould boil.</p>
    }else{
    return <p>The sater would not boil.</p>
    }
    }
    //温度之间的转换 两个函数
    function toCelsius(fahrenheit){
    return (fahrenheit - 32)*5/9;
    }

    function toFahrenheit(celsius){
    return (celsius * 9 / 5)+32;
    }

    //一个函数可以接受温度和转换温度的函数
    function tryConvert(temperature,convert){
    const input = parseFloat(temperature);
    if(Number.isNaN(input)){
    return '';
    }
    const output = convert(input);
    const rounded = Math.round(output*1000)/1000;
    return rounded.toString();
    }

    //创建一个用于输入温度的input
    const scaleNames = {
    c : 'Celsius',
    f : 'Fahrenheit'
    }
    class TemperatureInput extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    temperature:''
    };
    this.handleChange = this.handleChange.bind(this);
    }
    handleChange(e){
    // this.setState({
    // temperature:e.target.value
    // })
    this.props.onTemperatureChange(e.target.value);
    }
    render(){
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return(
    <fieldset>
    <legend>Enter temperature in {scaleNames[scale]}:</legend>
    <input value={temperature} onChange={this.handleChange} />
    </fieldset>
    )
    }
    }

    class Calculator extends React.Component{
    constructor(props){
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
    }
    handleCelsiusChange(temperature){
    this.setState ({
    scale: 'c',
    temperature
    })
    }
    handleFahrenheitChange(temperature){
    this.setState ({
    scale: 'f',
    temperature
    })
    }
    render(){
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ?tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
    return(
    <div>
    <TemperatureInput temperature={celsius} onTemperatureChange={this.handleCelsiusChange} scale='c' />
    <TemperatureInput temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} scale='f' />
    <BoilingVerdict celsius={parseFloat(celsius)} />
    </div>

    )
    }
    }
    ReactDOM.render(
    <Calculator />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    比较负责,好好看!!!再看一遍!!!主要是温度之间,只要改变,另一个就改变!!!

    10、组合 VS 继承
    10.1.包含关系
    有些组件无法提前知晓它们子组件的具体内容,我们建议用一个特殊 children prop来将他们的子组件渲染到结果中:
    如21demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
    .FancyBorder-yellow{
    color: yellow;
    }
    </style>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function FancyBorder(props){
    return(
    <div className={'FancyBorder FancyBorder-'+props.color}>
    //如果是类组件,就要加this.props.children
    {props.children}
    </div>
    )
    }
    function WelcomeDialog(){
    return(
    <FancyBorder color="yellow">
    <h1>Welocome</h1>
    <p>I love u dk zhang !</p>
    </FancyBorder>
    )
    }
    ReactDOM.render(
    <WelcomeDialog />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>
    这个例子是告诉我们,当我们引入组件并且里面传入DOM的时候,如果不用{props.children},里面是什么都不显示,
    所以必须用{props.children}来包裹住,有点像是VUE的插槽,但是React里面没有插槽的概念。
    当然,children不是固定的,如果我们父元素里面又引入了两个子组件,那么我们可以给他们分别起名字,然后再引入:
    function SplitPane(props) {
    return (
    <div className="SplitPane">
    <div className="SplitPane-left">
    {props.left}
    </div>
    <div className="SplitPane-right">
    {props.right}
    </div>
    </div>
    );
    }

    function App() {
    return (
    <SplitPane
    left={
    <Contacts />
    }
    right={
    <Chat />
    } />
    );
    }

    10.2.特例关系
    有时候,我们会把一些组件看做是其它组件的特殊实例,在React中,特殊组件可以通过props定制并渲染一般组件,如22demo:
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    function FancyBorder(props){
    return(
    <div className={'FancyBorder FancyBorder-'+props.color}>
    {props.children}
    </div>
    )
    }
    function Dialog(props){
    return(
    <FancyBorder color="blue">
    <h1>{props.title}</h1>
    <p>{props.message}</p>
    {props.children}
    </FancyBorder>
    )
    }
    //这是第一种写法
    // function WelcomeDialog(){
    // return(
    // <Dialog title="Welocome" message="dk zhang" />
    // )
    // }
    //第二种class写法
    class WelcomeDialog extends React.Component{
    constructor(props){
    super(props);
    this.state = {
    login : ''
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSignUp = this.handleSignUp.bind(this);
    }
    handleChange(e){
    this.setState({
    login : e.target.value
    })
    }
    handleSignUp(){
    alert(`Welcome aboard,${this.state.login}`)
    }
    render(){
    return(
    <Dialog title="Welocome" message="dk zhang">
    <input type="text" onChange={this.handleChange} value={this.state.login} />
    <button onClick={this.handleSignUp}>Sign Me Up !</button>
    </Dialog>
    )
    }
    }
    ReactDOM.render(
    <WelcomeDialog />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    22demo后期【200413】做了微调,可以康康~
    下面做一个大demo,做一个搜索功能 23demo:

    11、dangerouslySetInnerHTML 和 style属性
    11.1.当我们在防止XSS【脚本】攻击的时候,希望获取到某个表达式的内容,但React.js会把表达式里面的标签给转义,从而达不到我们的需求,这就需要用到dangerouslySetInnerHTML了!
    例如:由于这里是后来添加的,所以没有demo,直接写下面吧:
    class Editor extends Component {
    constructor() {
    super()
    this.state = {
    content: '<h1>React.js 小书</h1>'
    }
    }

    render () {
    return (
    <div className='editor-wrapper'>
    {this.state.content} //<h1>React.js 小书</h1>
    </div>
    )
    }
    }
    这个时候,我们可以得到的是,他渲染出来的带有标签,那我们加上dangerouslySetInnerHTML:
    class Editor extends Component {
    constructor() {
    super()
    this.state = {
    content: '<h1>React.js 小书</h1>'
    }
    }

    render () {
    return (
    <div className='editor-wrapper'>
    dangerouslySetInnerHTML={{__html:this.state.content}}
    </div>
    )
    }
    }
    这样就可以了!不过我们不推荐使用~~~

    11.2.React.js里面的style和普通的是不一样的:
    <h1 style='font-size: 12px; color: red;'>React.js 小书</h1>
    我们react里面,要先变成一个对象,再传进去,并且不能有带【-】的属性,统一改成驼峰式命名:
    <h1 style={{fontSize: '12px', color: 'red'}}>React.js 小书</h1>
    我们可以用 props 或者 state 中的数据生成样式对象再传给元素,然后用 setState 就可以修改样式:
    <h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小书</h1>


    12、react的生命周期函数

    我们把react.js将组件渲染,并且构造DOM元素然后塞入页面的过程,称为组件的挂载

    每一个组件都有自己的生命周期函数,当组件实例被创建并且会插入到DOM中的时候,下面周期函数会被调用:
    一、constructor
    二、componentWillMount
    三、render
    四、componentDidMount
    改变组件的state或props会导致更新,当重新渲染组件时会调用下面这些函数:
    一、componentWillReceiveProps
    二、shouldComponentUpdate
    三、componentWillUpdate
    四、render
    五、componentDidUpdate
    当组件从DOM中移除,会调用下面函数:
    一、componentWillUnmount

    下面我们一一说明:
    12.1.render() ---- render方法是必须的,render的返回值是下面的类型:
    (1)react元素:要么是自定义的组件,要么是原生的DOM组件
    (2)字符串或者数字:会被渲染成DOM中的文本节点
    (3)portals:通过ReactDom.createPortal创建
    (4)null:什么都不会渲染
    (5)Bollean:什么都不渲染
    (6)包含多个元素的数组 ---- render(){return [<li key='1'>1</li>,<li key='1'>1</li>]}
    render方法应该是简单的,在render中【不能】修改组件的【state】,每一次调用render都会返回一个新的结果。并且在render中也【不能】与【浏览器】进行交互,
    如果需要与浏览器交互,就在【componentDidMount】或者其他生命周期函数中进行。

    12.2.constructor(props) --- react组件的构造函数在组件装载之前调用。如果没有显示的定义construcor,那么实例化组件时会调用默认的constructor,
    如果在React.Component的子类中显示的定义了constructor,那么就要在constructor中最开始调用super(props)。
    在构造函数实例化state是一个很好的选择。
    在react中使用props初始化state是合法的,但是这存在一个问题:当props被更新时,state并不会被更新。解决的办法是:在组件的componentWillReceiveProps(nextProps)
    中用新的props更新state。虽然这能解决问题,但是并不推荐,推荐把state提升到最近的公共父组件中。

    12.3.componentWillMount() ---- 当装载发生之前会立即调用componentWillMount。componentWillMount会在调用render之前被调用,
    所有在componentWillMount中修改的state,不会导致组件的重新渲染。服务器端渲染才会调用这个方法,
    所以推荐通过constructor代替这个方法。

    12.4.componentDidMount() ---- 当组件被装载完成会立即出发componentDidMount,在这个函数中修改state会导致组件重新渲染。组件被装载之后才能
    操作DOM。如果你需要加载远程数据,在这个地方发送网络请求是个不错的主意。

    12.5.componentWillReceiveProps(nextProps) --- 当已经被转载的组件接受新的props之前componentWillReceiveProps会被触发。如果你需要更新state
    去响应props的更新,可以在这里通过setState方法更新state。当组件首次接受到props,这个方法不会被调用。
    注意:props没有被改变也可能会调用这个方法,所以在这个方法中将当前的props去next props进行比较是很有必要的。

    12.6.shouldComponentUpdate(nextState,nextProps) --- 当新的props或state被接受,在渲染之前会调用shouldComponentUpdate,这个方法默认是返回true,
    初次渲染和使用forceUpdate,不会调用这个方法。如果shouldComponentUpdate返回false,之后的
    componentWillUpdate,render以及componentDidMount不会被盗用,组件以及他的子组件不会被重新渲染。

    12.7.componentWillUpdate(nextProps,nextState) --- 当接受到新的props或state,在重新渲染之前会立即调用这个方法。在这个方法中不能this.setState()
    初次渲染不会调用这个方法。

    12.8.componentDidMount(prevProps,prevState) --- 当更新完成之后会立即调用这个方法,初次渲染不会调用这个方法。当组件被更新之后可以在这里操作DOM,
    当你发现现在的props与之前的props不一样,在这个发送网络请求是个不错的主意。

    12.9.componentWillUnmount() ---- 组件被摧毁之前会立即调用这个方法,可以在这个方法中做一些必要的清理。

    然后我们要明白他和vue的生命周期函数的区别:
    beforeCreate:el和data并未初始化;
    created:完成了data数据的初始化,el没有
    beforeMount:完成了el和data初始化
    mounted:完成挂载
    beforeUpdate:注意这里是指view层的数据变化前,不是data中的数据变化前触发。
    updated:更新完成
    beforeDestroy:销毁前
    destroyed:当前组件被删除,清空相关内容 ---- 注意,销毁完成后,我们再重新改变message的值,vue不在对此动作进行响应了。但是原先生成的dom元素还在,
    可以这么理解:执行了destroy操作,后续就不再受vue控制了,因为这个vue实例已经不存在了。

    我们写一个时钟的例子,这里用到了componentWillMount和componentWillUnmount
    如27demo,这里主要注意的就是一个清楚定时器的操作


    13、PropType -- 可以设置组件的属性值的属性,比如阮一峰的demo06 ,我们这里举一个例子,加入我们控制了组件的title属性必须是字符串24demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script src="../gitRuan/react-demos-master/build/prop-types.js"></script>
    <script type="text/babel">

    class Title extends React.Component{
    static propTypes = {
    title:PropTypes.string.isRequired,
    }
    render(){
    return(
    <div>
    <h1>这里来个标题</h1>
    <h2>TITLE: {this.props.title}.</h2>
    </div>
    )
    }
    }

    var title = '111';
    ReactDOM.render(
    <Title title={title} />,
    document.getElementById('root')
    )
    </script>
    </body>

    这里要注意 想要用propTypes,就必须引入相应的js,或者是cnpm相应的依赖 不然是会报错的:peoptype is not defined!!!
    此外,getDefaultProps方法可以设置属性的默认值,如25demo
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class Title extends React.Component{
    render(){
    return(
    <div>
    <h1>这里来个标题</h1>
    <h2>TITLE: {this.props.title}.</h2>
    </div>
    )
    }
    }
    Title.defaultProps = {
    title : 'hello world'
    }
    ReactDOM.render(
    <Title />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这里要注意 defaultProps只能写在class外 这是es6的规定 另外写的时候 也不是getDefaultProps 而是defaultProps 切记!

    14、获取真实的DOM
    组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,焦作虚拟DOM(virtual DOM)。只有当它插入文档后,才会变成真实的DOM。
    根据React的设计,所有DOM变动,都现在虚拟DOM上发生,然后再将实际发生变动的部分,反映在真实DOM上,这种算法叫做DOM diff。
    但是,有时候需要从组建获取真实DOM的节点,这就需要用到ref属性 26demo <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <div id="root"></div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <!-- 生产环境中不建议使用 -->
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    <script type="text/babel">
    class Title extends React.Component{
    constructor(props) {
    super(props);
    this.myTextInput = React.createRef();
    this.handleClick = this.handleClick.bind(this)
    }
    handleClick(){
    this.refs.myTextInput.focus();
    }
    render(){
    return(
    <div>
    <h1>这里来个输入框</h1>
    <input type="text" ref="myTextInput" />
    <input type="button" value="inputGetFoucus" onClick={this.handleClick} />
    </div>
    )
    }
    }
    ReactDOM.render(
    <Title />,
    document.getElementById('root')
    )
    </script>
    </body>
    </html>

    这里注意,要用ref的时候,要在constructor里面先声明一下,也就是【React.createRef()】

    我们可以借用ref来实现点击空白弹窗消失的功能,用到了contains()方法
    contains 判断某个元素是不是选定元素的子元素或者本身。
    https://blog.csdn.net/boysky0015/article/details/88933184

    15、项目实战
    15.1.todoList 一个输入后插入删除的小demo
    react-todoList小demo--https://juejin.im/post/5c945755f265da611c557076
    是myProject
    我们按照这个demo来做一遍:

    1、安装脚手架:cnpm i create-react-app -g
    2、查看自己是否安装脚手架:create-react-app --version
    3、创建项目名:create-react-app todolist
    4、进入项目:cd todolist
    5、跑项目:cnpm start
    6、打开 localhost:3000 查看页面

    15.2.评论功能 --- commentapp 来源:http://huziketang.mangojuice.top/books/react/lesson14
    首先也是 create-react-app commentapp
    然后进去commentapp

    这里报了一个错:npm ERR! Unexpected end of JSON input while parsing near
    本来教程又说要去删除一些文件:C:Users当前计算机用户名字AppDataRoaming pm-cache
    但是经我发现,只需要将npm永远指向淘宝镜像就ok了,也就是:
    npm config set registry https://registry.npm.taobao.org
    -- 配置后可通过下面方式来验证是否成功
    npm config get registry
    -- 或npm info express
    如果不成功,再使用上述方法

    我们遵循一个原则:如果一个文件导出的是一个类,那么这个文件名就用大写开头。四个组件类文件导出都是类,所以都是大写字母开头。

    16、代码分割
    15.1.React.lazy & Suspense
    相当于是懒加载,用的时候要先引入插件
    import {lazy} from React
    我们写一个例子,就在我们的todolist项目吧
    我们新建一个testLazy1.js和testLazy2.js,里面的内容是一样的:
    import React from 'react';
    // Fragment 是一种占位符形式,类似于 Vue 的 Template
    import {Component} from 'react';


    class TestLazy1 extends Component{
    render(){
    return(
    <div>
    <h1>LazyTest 组件</h1>
    </div>
    )
    }
    }
    export default TestLazy1;
    然后在Todolist.js里面通过lazy的方式引入:
    import React from 'react';
    // Fragment 是一种占位符形式,类似于 Vue 的 Template
    import {Component,Fragment,lazy,Suspense} from 'react';

    //引入组件
    import Todoitem from './Todoitem.js'


    //懒加载试验
    const TestLazy1 = lazy(() => import('./components/testLazy1.js'));
    const TestLazy2 = lazy(() => import('./components/testLazy2.js'));

    class Todolist extends Component{
    //懒加载函数
    fallback=()=>{
    return(
    <div>Loadin··</div>
    )
    }
    constructor(props){
    super(props);
    this.state={
    inputValue:'',
    list:[]
    }
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
    }
    handleChange(e){
    console.log(e.target.value);
    const value = e.target.value;
    this.setState(()=>({
    inputValue:value
    }))
    }
    handleClick(){
    const list = this.state.list;
    const inputValue = this.state.inputValue;
    this.setState(()=>({
    list:[...list,inputValue],
    inputValue:''
    }))
    }
    handleDelete(index){
    //加三个···相当于拼接
    const list = [...this.state.list];
    list.splice(index,1);
    this.setState(()=>({
    list:list
    }))
    }
    getTodoItem(){
    return this.state.list.map((item,index)=>{
    return(
    <Todoitem key={index} index={index} item={item} handleDelete={this.handleDelete}></Todoitem>
    )
    })
    }
    render(){
    return(
    <Fragment>
    <div>
    <input type="text" value={this.state.inputValue} onChange={this.handleChange} />
    <button onClick={this.handleClick}>提交</button>
    </div>
    <ul>
    {this.getTodoItem()}
    </ul>
    <div>
    <Suspense fallback={this.fallback()}>
    <h1>懒加载组件</h1>
    <TestLazy1 />
    <TestLazy2 />
    </Suspense>
    </div>
    </Fragment>
    )
    }
    }

    export default Todolist;
    然后执行:cnpm run build【注意和cnpm start不一样,这是打包】,发现虽然是同样的内容,但是他们生成了两个js,分别是:
    3.fae2c000.chunk.js和4.a0ae15a6.chunk.js

    方便起见,我们还是把build给删掉,以后需要的时候再打包

    17、状态提升
    当我们的同级组件或者是其他地方需要用到这个组件的属性的时候,我们要选择用状态提升,把这个属性提升到和其它组件共同的父组件中,用props提供,再用props获取

    18、propTypes 和组件参数验证
    当我们获取参数的时候,往往因为没有设置类型而出现bug,而在js这种弱类型语言中不会报错,只会运行错误,但React这种就会直接报错,因为他支持强类型语言。js为了弥补缺陷,开发了typescript,而react为了规避这个问题,就要引入一个插件:cnpm install prop-types --save
    然后就可以在static propTypes里面设置类型:
    import React, { Component } from 'react'
    import PropTypes from 'prop-types'

    class Comment extends Component {
    static propTypes = {
    comment: PropTypes.object
    }

    render () {
    const { comment } = this.props
    return (
    <div className='comment'>
    <div className='comment-user'>
    <span>{comment.username} </span>:
    </div>
    <p>{comment.content}</p>
    </div>
    )
    }
    }
    有的时候,我们虽然传入元素了,但是不一定每一个参数都有属性值,有时候可能没有传入,这个时候就会报错说没有值或者undefined。
    这个时候,我们就可以用过isRequired来强制组件的某个参数必须传入:
    ...
    static propTypes = {
    comment: PropTypes.object.isRequired
    }
    ...
    这样虽然不会避免报错,但是可以让我们报错的内容更加直观明朗

    而且我们可以通过设置defaultProps来设置默认参数值,比如25demo:
    class Title extends React.Component{
    render(){
    return(
    <div>
    <h1>这里来个标题</h1>
    <h2>TITLE: {this.props.title}.</h2>
    </div>
    )
    }
    }
    Title.defaultProps = {
    title : 'hello world'
    }
    ReactDOM.render(
    <Title />,
    document.getElementById('root')
    )


    绑定事件传参的方法:https://blog.csdn.net/hl_qianduan/article/details/89372881

    19、高阶组件
    高阶组件就是一个函数【不是组件】,传给他一个组件,它返回一个新的组件。
    新的组件使用传入的组件作为子组件。
    高阶组件的作用是用于代码复用,可以把组件之间可复用的代码、逻辑抽离到高阶组件当中。新的组件和传入的组件通过props传递信息

    高阶组件有助于提高我们的代码灵活性、逻辑复用性。
    具体实操还是看我们的评论小项目,就是我们的commentapp项目
    实际上就是把getItem setItem这样的数据,或者是用ajax调用的,变成一个组件,然后重复利用

    20、context
    一个组件可以通过getChildContext方法返回一个对象,这个对象就是子树的context,提供context的组件必须提供childContextTypes作为context的声明和验证。
    如果一个组件设置了context,那么它的子组件都可以直接访问到里面的内容,他就像这个组件为根的子树的全局变量。
    任意深度的子组件都可以通过contextTypes来生命你想要的context里面的哪些状态,然后可以通过this.context访问到那些状态
    假设我们的组件关系是这样的:
    class Index extends Component {
    static childContextTypes = {
    themeColor:PropTypes.string
    }
    constructor(props){
    super(props)
    this.state={
    themeColor:red
    }
    }

    getChildContext(){
    return{
    themeColor:this.state.themeColor
    }
    }

    render () {
    return (
    <div>
    <Header />
    <Main />
    </div>
    )
    }
    }

    class Header extends Component {
    render () {
    return (
    <div>
    <h2>This is header</h2>
    <Title />
    </div>
    )
    }
    }

    class Main extends Component {
    render () {
    return (
    <div>
    <h2>This is main</h2>
    <Content />
    </div>
    )
    }
    }

    class Title extends Component {
    static ContextTypes = {
    themeColor:PropTypes.string
    }
    render () {
    return (
    <h1 style={{color:this.context.themeColor}}>React.js 小书标题</h1>
    )
    }
    }

    class Content extends Component {
    render () {
    return (
    <div>
    <h2>React.js 小书内容</h2>
    </div>
    )
    }
    }

    ReactDOM.render(
    <Index />,
    document.getElementById('root')
    )

    21、redux react-redux
    注意redux和react-redux是不一样的,redux只是代表一种架构模式(Flux架构的一种变种),他不关注你到底用什么库,可以是react,也可以是vue,甚至可以是jquery
    而react-redux就是redux这种架构模式和react.js结合起来的一个库,就是redux架构在react.js中的体现。

    21.1.我们通过项目来学习redux,创建一个叫make-redux,
    我们修改index里面的结构,修改index.js里面的内容

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
    name="description"
    content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
    </head>
    <body>
    <div id="root">
    <div id="title"></div>
    <div id="content"></div>
    </div>
    </body>
    </html>


    import './index.css';
    let appState = {
    title:{
    text:'React.js 小书',
    color:'red',
    },
    content:{
    text:'React.js 小书内容',
    color:'blue',
    }
    }

    function dispatch(action){
    switch(action.type){
    case 'UPDATE_TITLE_TEXT':
    appState.title.text = action.text
    break
    case 'UPDATE_TITLE_COLOR':
    appState.title.color = action.color
    break
    default:
    break
    }
    }

    function renderApp(appState){
    renderTitle(appState.title);
    renderContent(appState.content);
    }

    function renderTitle(title){
    const titleDOM = document.getElementById('title');
    titleDOM.innerHTML = title.text;
    titleDOM.style.color = title.color
    }

    function renderContent(title){
    const contentDOM = document.getElementById('content');
    contentDOM.innerHTML = title.text;
    contentDOM.style.color = title.color
    }

    renderApp(appState);//首次渲染页面
    dispatch({type:'UPDATE_TITLE_TEXT',text:'《React.js小书》'})//修改标题文本
    dispatch({type:'UPDATE_TITLE_COLOR',color:'blue'})//修改标题颜色
    renderApp(appState);//把新的数据渲染到页面上

    然后我们把dispatch和appState给放到store里面,怎么放呢?就是添加一个createStore函数在index.js,他是串门用来生产state和dispatch的集合:
    function createStore (state,statechanger){
    const getState = ()=> state
    const dispatch = (action) => stateChanger(state,action)
    return {getState,dispatch}
    }

    然后渲染数据的方式不变,修改数据生成的方式
    const store = createStore(appState,stateChanger);

    renderApp(store.getState());//首次渲染页面
    store.dispatch({type:'UPDATE_TITLE_TEXT',text:'《React.js小书》'})//修改标题文本
    store.dispatch({type:'UPDATE_TITLE_COLOR',color:'blue'})//修改标题颜色
    renderApp(store.getState());//把新的数据渲染到页面上

    我们同时有希望这个数据是可以自己检测到变化而自动更新的,而不是每次都要调用一次renderApp()。那就是用监听模式:
    import './index.css';

    let appState = {
    title:{
    text:'React.js 小书',
    color:'red',
    },
    content:{
    text:'React.js 小书内容',
    color:'blue',
    }
    }

    function stateChanger(state,action){
    switch(action.type){
    case 'UPDATE_TITLE_TEXT':
    state.title.text = action.text
    break
    case 'UPDATE_TITLE_COLOR':
    state.title.color = action.color
    break
    default:
    break
    }
    }

    function createStore (state,stateChanger){
    const listeners = [];
    const subscribe = (listener) => listeners.push(listener);
    const getState = ()=> state
    const dispatch = (action) => {
    stateChanger(state,action)
    listeners.forEach((listener)=>listener())
    }
    return {getState,dispatch,subscribe}
    }

    function renderApp(appState){
    renderTitle(appState.title);
    renderContent(appState.content);
    }

    function renderTitle(title){
    const titleDOM = document.getElementById('title');
    titleDOM.innerHTML = title.text;
    titleDOM.style.color = title.color
    }

    function renderContent(title){
    const contentDOM = document.getElementById('content');
    contentDOM.innerHTML = title.text;
    contentDOM.style.color = title.color
    }

    const store = createStore(appState,stateChanger);
    store.subscribe(()=>renderApp(store.getState()))//监听数据变化

    renderApp(store.getState());//首次渲染页面
    store.dispatch({type:'UPDATE_TITLE_TEXT',text:'《React.js小书》'})//修改标题文本
    store.dispatch({type:'UPDATE_TITLE_COLOR',color:'blue'})//修改标题颜色
    //renderApp(store.getState());//把新的数据渲染到页面上

    完整的index.js:
    import './index.css';
    function reducer(state,action){
    if(!state){
    return{
    title:{
    text:'React.js 小书',
    color:'red',
    },
    content:{
    text:'React.js 小书内容',
    color:'blue',
    }
    }
    }
    switch(action.type){
    case 'UPDATE_TITLE_TEXT':
    return{//构建新的对象并返回
    ...state,
    title:{
    ...state.title,
    text:action.text
    }
    }
    case 'UPDATE_TITLE_COLOR':
    return{//构建新的对象并返回
    ...state,
    title:{
    ...state.title,
    color:action.color
    }
    }
    default:
    return state//没有修改,返回原来的对象
    }
    }

    function createStore (reducer){
    let state = null;
    const listeners = [];
    const subscribe = (listener) => listeners.push(listener);
    const getState = ()=> state
    const dispatch = (action) => {
    state=reducer(state,action) //覆盖原对象
    listeners.forEach((listener)=>listener())
    }
    dispatch({});//初始化state
    return {getState,dispatch,subscribe}
    }

    function renderApp(newAppState,oldAppState=
    {}){
    if(newAppState === oldAppState)return //数据没有变化就不渲染了
    console.log('render App');
    renderTitle(newAppState.title);
    renderContent(newAppState.content);
    }

    function renderTitle(newTitle,oldTitle={}){
    if(newTitle === oldTitle)return //数据没有变化就不渲染了
    console.log('render Title');
    const titleDOM = document.getElementById('title');
    titleDOM.innerHTML = newTitle.text;
    titleDOM.style.color = newTitle.color
    }

    function renderContent(newContent,oldContent={}){
    if(newContent === oldContent)return //数据没有变化就不渲染了
    console.log('render content');
    const contentDOM = document.getElementById('content');
    contentDOM.innerHTML = newContent.text;
    contentDOM.style.color = newContent.color
    }

    const store = createStore(reducer);
    let oldState = store.getState();
    store.subscribe(()=>{
    const newState = store.getState();//数据可能变化,获取新的state
    renderApp(newState,oldState)//把新旧的state传进去渲染
    oldState = newState; // 渲染完以后,新的newState变成了旧的oldState,等待下一次数据变化重新渲染
    })//监听数据变化

    renderApp(store.getState());//首次渲染页面
    store.dispatch({type:'UPDATE_TITLE_TEXT',text:'《React.js小书》'})//修改标题文本
    store.dispatch({type:'UPDATE_TITLE_COLOR',color:'blue'})//修改标题颜色
    //renderApp(store.getState());//把新的数据渲染到页面上

    21.2.react-redux
    其实这里就是把context和store结合的地方
    首先我们还是新建一个create-react-app react-redux
    进入项目:cd react-redux
    然后引入:cnpm install prop-types --save

    21.2.1.import引入时的{}问题
    当我们引入的是export default XX;类型的文件时,可以不用{}
    而且可以自己随意起名字,比如:
    import A from 'a.js';
    import B from 'a.js';
    import fvbbg from 'a.js'
    都是成立的;

    但是我们是命名导出为export name的模块时,就要加{}了:
    export const A = ···

    react-redux这个项目也是为了了解react和redux结合
    自己写了createStore 写了themeReducer
    在createStore里面做了监听listener和刷新subscribe
    另外我们为了Index里面没有context相关,又加了provider在react-redux.js里面
    而react-redux里面除了provider之外,还有connect方法,是为了做高阶组件从而使得props传承

    22、真正的在项目中使用redux react-redux插件
    首先我们再创立一个新的react项目,叫make-react-redux
    create-react-app make-react-redux
    cd make-react-redux
    安装redux 和 react-redux插件:
    cnpm install redux react-redux --save
    然后把index.js Header.js Content.js themeSwitch.js react-redux.js 全部复制进去,再修改Header.js Content.js themeSwitch.js里面的connect引入方法:
    由import { connect } from './react-redux'; 变成:
    import { connect } from 'react-redux';

    我们把组件分为Dumb组件,还有Smart组件
    凡是不依赖任何外部组件以及react-redux或者redux的组件,叫做Dumb组件,但是我们观察到,我们之前写的Header.js也好,content.js也好,都是本来是Dumb组件的,因为有了connect,从而用了react-redux,成了Smart组件

    import React,{Component} from 'react';
    import PropTypes from 'prop-types';
    import { connect } from 'react-redux';

    class Header extends Component{
    static propTypes = {
    themeColor:PropTypes.string,
    }

    render(){
    return(
    <h1 style={{color:this.props.themeColor}}>张卫健</h1>
    )
    }
    }

    const mapStateToProps = (state) => {
    return {
    themeColor:state.themeColor
    }
    }
    Header = connect(mapStateToProps)(Header)

    export default Header;

    为了避免这种问题,我们新建了一个components文件夹,约定它里面就放Dumb组件
    另外一个containers文件夹,放Smart组件

    Dumb 基本只做一件事情 —— 根据 props 进行渲染。而 Smart 则是负责应用的逻辑、数据,把所有相关的 Dumb(Smart)组件组合起来,通过 props 控制它们

    Smart 组件可以使用 Smart、Dumb 组件;而 Dumb 组件最好只使用 Dumb 组件,否则它的复用性就会丧失。

    22、用UI框架 【技术胖的视频学习】 --- Ant Design
    首先我们要重新开一个项目,然后引入antdesign:
    create-react-app antdesign-react
    cd antdesign-react
    cnpm install antd --save
    cnpm install redux --save

    我们用全局引入的话,就是在文件里面引入css和需要的组件,这都是全局引入:
    import 'antd/dist/antd.css';
    import { Input } from 'antd';
    import { Button } from 'antd';

    按需引入有两种方法,一个是引入的时候,就加上后缀:
    import DatePicker from 'antd/es/date-picker'; // 加载 JS
    import 'antd/es/date-picker/style/css'; // 加载 CSS
    // import 'antd/es/date-picker/style'; // 加载 LESS

    一个就是配置 babel-plugin-import
    cnpm install babel-plugin-import --save-dev
    然后在根目录下新建一个.babelrc文件,这里可以先建立一个txt文件,然后另存为.babelrc文件
    修改不了后缀名的话,就在文件夹的组织里面找到【文件夹和搜索选项】,然后【查看】在最下面找到展示后缀名
    然后添加如下:
    // .babelrc or babel-loader option
    {
    "plugins": [
    ["import", {
    "libraryName": "antd",
    "libraryDirectory": "es",
    "style": "css" // `style: true` 会加载 less 文件
    }]
    ]
    }
    然后引入的时候:
    // babel-plugin-import 会帮助你加载 JS 和 CSS
    import { DatePicker } from 'antd';

    我们想要用redux-devtool工具,就要在chrome里添加这个扩展程序,怎么添加呢?
    首先我们要安装一个插件,叫做 谷歌访问助手
    下载链接:https://github.com/haotian-wang/google-access-helper
    然后我们在本地新建一个文件夹,且就是:D:/chromeHelper
    然后把这个链接里面的地址 git clone上去,也即是:
    git clone https://github.com/haotian-wang/google-access-helper.git
    然后再打开chrome,点击右上角的红色箭头或者三个省略号,
    然后点击更多工具,然后是【扩展程序】,
    然后点击【加载已解压的扩展程序】,把已经下载好的谷歌访问助手加入进来
    点击【扩展程序】左边的三个横线,下面出来【打开chrome应用商店】
    去搜索redux-dev添加就可以了
    这个时候F12自己的项目,最后面就是redux工具
    到这里还不算完,还要在自己的store文件夹里面的index.js,也就是引入reducer的那个文件里,配置:
    const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
    然后再去看,就有了!
    如果我们点击state,就会看到目前所有的数据,也就不用再繁琐的console.log了!


    以前:安装babel-plugin-import后只需要按照antd文档中的方式在.babelrc中配置即可。

    现在:就算配置了.babelrc也不会按需引入,而且最重要的还不报错。
    1、运行 npm run eject (暴露react的配置,不然很多配置不会显示出来,包括我们现在需要的babel配置)
    可能会报错,就git status
    git add .
    git commit -m "init eject"
    git commit -m "init eject"
    cnpm run eject
    但是基本上我写了还是会报错,但是只要后面那是successful,可以运行就可以了!
    暂时不要在意这些细节!

    2、打开package.json文件,找到

    "babel": {
    "presets": [
    "react-app"
    ]
    添加antd文档中对于babel-plugin-import的配置,修改为:
    "babel": {
    "presets": [
    "react-app"
    ],
    "plugins":
    [
    [
    "import",
    {
    "libraryName": "antd",
    "libraryDirectory": "es",
    "style": "css"
    }
    ]
    ]
    3、确保src下没有.babelrc文件,重启项目,一切很OK;

    23、axios异步请求 重新建一个项目 在vscode里面运行,并且打开终端,并且使用ant的按需请求【另一种官方方法】
    axios的异步请求地址用的是技术胖的:https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList

    首先还是新建一个项目create-react-app axios-react
    cd axios-react
    复制antdesign-react-new项目
    cnpm install
    cnpm install axios --save
    cnpm start


    23.1.vscode打开终端只需要[ctrl+`]

    23.2.ant的按需请求
    按照官方文档:https://ant.design/docs/react/use-with-create-react-app-cn所讲
    cnpm install react-app-rewired customize-cra --save

    修改package.json文件:
    "scripts": {
    // "start": "react-scripts start",
    "start": "react-app-rewired start",
    // "build": "react-scripts build",
    "build": "react-app-rewired build",
    // "test": "react-scripts test",
    "test": "react-app-rewired test"
    // "eject": "react-scripts eject"
    // "eject": "react-scripts eject"
    },

    在项目根目录创建一个config-overrides.js,用于修改默认配置
    //module.exports = function override(config, env) {
    //// do stuff with the webpack config...
    //return config;
    //};

    const { override, fixBabelImports } = require('customize-cra');
    module.exports = override(
    fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
    }),
    );

    到这里就是成功的!

    但是我们为了后期使用ant方便,尤其是修改主题的时候要用到less,所以我们先给他配置成支持less的:
    cnpm install less less-loader --save

    const { override, fixBabelImports, addLessLoader } = require('customize-cra');
    module.exports = override(
    fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    // style: 'css',
    style: true,
    }),
    addLessLoader({
    javascriptEnabled: true,
    modifyVars: { '@primary-color': '#1DA57A' },
    }),
    );

    成功cnpm start之后,发现我们的主题色变成绿色了!

    以上我们完成了两个需求

    接下来完成我们的axios异步请求

    这里我们首先要在TodoList里面的componentDidMount里面写上axios请求
    使用中间件,redux-thunk
    cnpm install redux-thunk --save

    修改store里面的index配置
    import {createStore,applyMiddleware,compose} from 'redux'
    import reducer from './reducer'

    //引入thunk
    import thunk from 'redux-thunk'

    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose;
    const enhancer = composeEnhancers(applyMiddleware(thunk));

    //const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
    const store = createStore(reducer,enhancer);

    export default store;
    在没有使用中间件之前,我们的actionCreator只能返回对象,加了中间件,就可以返回一个函数

    中间件是redux的,不是react的,注意!

    redux-saga也是一种中间件!这里我就不再笔记demo了,如果公司有用,就直接 去技术胖的redux-react第19集!


    24、react-redux项目 新建react-redux-app
    create-react-app react-redux-app;
    cd react-redux-app;
    cnpm install;
    cnpm install redux react-redux --save

    当我们用到connect的时候,主要要有一个映射,也就是我们的stateToProps函数,他就是指我们把state属性转化为props,以便在jsx里this.props.XXX变成this.props.XXX
    import { connect } from 'react-redux'
    const VisibleTodoList = connect()(TodoList);
    上面代码中,TodoList是 UI 组件,VisibleTodoList就是由 React-Redux 通过connect方法自动生成的容器组件。
    但是,因为没有定义业务逻辑,上面这个容器组件毫无意义,只是 UI 组件的一个单纯的包装层。为了定义业务逻辑,需要给出下面两方面的信息。
    输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
    输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

    Provider:用过之后,App的所有子组件就默认都可以拿到state了。

    如果我们用到了this.props.XXX方法,但是不知道怎么传参数,就这样:<span onClick={()=>{this.props.deleteList(index)}} value={index}> ==删除</span>

    我们还可以通过结构赋值,来替换this.props.inputValue等
    但是这里就不再替换了,但是要知道是这样的:
    let { inputValue , addList , deleteList , list } = this.props;

    24、路由 reactRouter
    首先,还是创建一个新的项目
    create-react-app router-react-app07
    cd router-react-app07
    cnpm install react-router-dom --save
    cnpm install redux react-redux --save

    然后,一个最简单的方式:
    import React from 'react';
    import {BrowserRouter as Router,Route,Link} from 'react-router-dom';

    function App(){
    return (
    <div className="App">
    sadf sadf
    </div>
    );
    }

    function List(){
    return (
    <h2>我是list</h2>
    )
    }

    function AppRouter(){
    return (
    <Router>
    <ul>
    <li><Link to='/'>首页</Link></li>
    <li><Link to='/list/'>列表</Link></li>
    </ul>
    <Route path='/' exact component={App} />
    <Route path='/list/' exact component={List} />
    </Router>
    )
    }
    export default AppRouter;

    24.1.重定向
    重定向可以让你的页面打开时,不能再前进后退
    import React,{Component} from 'react';
    import {Link,Redirect} from 'react-router-dom';

    class Index extends Component{
    constructor(props){
    super(props);
    this.state={
    list:[
    {cid:123,title:'刘璐的博客-1'},
    {cid:456,title:'刘璐的博客-2'},
    {cid:789,title:'刘璐的博客-3'}
    ]
    }
    }
    render(){
    return(
    <div>
    //必须放在return里面
    <Redirect to="/home/" />
    <h2>我是首页</h2>
    <ul>
    {
    this.state.list.map((item,index)=>{
    return(
    <li key={index}>
    <Link to={'/list/'+item.cid}>
    {item.title}
    </Link>
    </li>
    )
    })
    }
    </ul>
    </div>
    )
    }
    }
    export default Index;

    24.2.编程式重定向
    import React,{Component} from 'react';
    import {Link,Redirect} from 'react-router-dom';

    class Index extends Component{
    constructor(props){
    super(props);
    this.state={
    list:[
    {cid:123,title:'刘璐的博客-1'},
    {cid:456,title:'刘璐的博客-2'},
    {cid:789,title:'刘璐的博客-3'}
    ]
    }
    this.props.history.push('/home/')
    }
    render(){
    return(
    <div>
    //必须放在return里面
    //<Redirect to="/home/" />
    <h2>我是首页</h2>
    <ul>
    {
    this.state.list.map((item,index)=>{
    return(
    <li key={index}>
    <Link to={'/list/'+item.cid}>
    {item.title}
    </Link>
    </li>
    )
    })
    }
    </ul>
    </div>
    )
    }
    }

    export default Index;

    25、嵌套路由
    这里我就不再用redux了
    我们做一个真正的小demo,就是router-react-pro07
    create-react-app router-react-pro07
    cd router-react-pro07
    cnpm install react-router-dom --save

    25.2.动态获取路由进行配置
    这意思是后台传给我们路由,而不是自己写死的三个或者四个,这个在07里面有实现


    26、react hooks --- 不用class声明组件,而是用函数方法声明组件
    首先我们新建一个文件夹,叫react-hooks-demo08
    然后我们要注意,用hooks,就要用useState,这要求我们的版本一定是16.8以上的
    然后我们首先写一个点击数量加一的例子,也就是我们的app.js里面的例子,里面必须先引入{useState}

    26.1.useState
    const [count , setCount] = useState(0);
    上面就是在应用useState的实例
    useState(0)里面的0就是一个初始值
    而const [count , setCount]代表es6的数组结构,如果不用es6,就要写成:
    let _useState = useState(0);
    let count = _useState[0];
    let setCount = _useState[1];

    为了更清楚的了解,我们建立Example2.js
    import React,{useState} from 'react';function Example2(){
    const [age , setAge] = useState(24);
    const [sex , setSex] = useState('female');
    const [work , setWork] = useState('web前端');

    return (
    <div>
    <p>刘璐今年:{age}岁</p>
    <p>刘璐是一个:{sex}孩子</p>
    <p>刘璐是一名:{work}职业</p>
    </div>
    )
    }

    export default Example2;

    26.2.用useEffect()代替生命周期
    首先我们新建一个example3.js
    事实上,在example3.js里面,他对应的应该是下面两个周期函数:
    componentDidMount(){
    console.log(`componentDidMount => You clicked ${this.state.times} times`)
    }
    componentDidUpdate(){
    console.log(`componentDidUpdate => You clicked ${this.state.times} times`)
    }
    统一变成了:
    useEffect(()=>{
    console.log(`useEffect => You clicked ${time} times`)
    })

    当我们写成这样的时候:
    import React,{useState,useEffect} from 'react';
    import {BrowserRouter as Router, Route, Link} from 'react-router-dom';

    function OtherIndex(){
    useEffect(()=>{
    console.log('useEffect => 老弟,你来了我们OtherIndex页面!');
    return ()=>{
    console.log('老弟,你走了我们OtherIndex页面!')
    }
    })
    return(
    <div>我是另一个主页</div>
    )
    }

    function OtherList(){
    useEffect(()=>{
    console.log('useEffect => 老弟,你来了我们OtherList页面!!');
    return ()=>{
    console.log('老弟,你走了我们OtherList页面!')
    }
    })
    return(
    <div>我是一个列表页</div>
    )
    }

    function Example3(){
    const [time,setTime] = useState(0);
    useEffect(()=>{
    console.log(`useEffect => You clicked ${time} times`)
    })
    return(
    <div>
    <p>u already clicked {time} times.</p>
    <button onClick={()=>{setTime(time+1)}}>点击加一</button>
    </div>
    )
    }

    function RouterIndex(){
    return(
    <Router>
    <ul>
    <li><Link to='/OtherIndex'>OtherIndex</Link></li>
    <li><Link to='/OtherList'>OtherList</Link></li>
    </ul>
    <Route path='/OtherIndex' component={OtherIndex} />
    <Route path='/OtherList' component={OtherList} />
    </Router>
    )
    }

    export default RouterIndex;

    当我们点击OtherList的时候,会一直出现老弟,你走了我们OtherIndex页面!说明他不只有在销毁的时候出现,而是只要不在这个页面,都会出现,这显然不符合我们的要求

    然后我们新建一个example4.js,升级example3.js
    import React,{useState,useEffect} from 'react';
    import {BrowserRouter as Router, Route, Link} from 'react-router-dom';

    function OtherIndex(){
    useEffect(()=>{
    console.log('useEffect => 老弟,你来了我们OtherIndex页面!');
    return ()=>{
    console.log('老弟,你走了我们OtherIndex页面!')
    }
    },[])//这里加一个空的数组的参数,表明我们只有解绑的时候,才会出现销毁提示,只有第一次进来的时候,才会出现提示
    return(
    <div>我是另一个主页</div>
    )
    }

    function OtherList(){
    useEffect(()=>{
    console.log('useEffect => 老弟,你来了我们OtherList页面!!');
    return ()=>{
    console.log('老弟,你走了我们OtherList页面!')
    }
    },[])
    return(
    <div>我是一个列表页</div>
    )
    }

    function RouterIndex(){
    return(
    <Router>
    <ul>
    <li><Link to='/OtherIndex'>OtherIndex</Link></li>
    <li><Link to='/OtherList'>OtherList</Link></li>
    </ul>
    <Route path='/OtherIndex' component={OtherIndex} />
    <Route path='/OtherList' component={OtherList} />
    </Router>
    )
    }

    export default RouterIndex;

    但是我们的点击次数加一的例子来讲,他必须每次都出现,假如我们给他一个空数组,他就只出现一次,怎么办呢?
    这个时候,就要注意了,虽然我们可以传入空数组,但是同时,我们也可以不传入空数组啊,我们给他一个传值就OK拉!
    function Example3(){
    const [time,setTime] = useState(0);
    useEffect(()=>{
    console.log(`useEffect => You clicked ${time} times`)
    },[time])
    return(
    <div>
    <p>u already clicked {time} times.</p>
    <button onClick={()=>{setTime(time+1)}}>点击加一</button>
    </div>
    )
    }

    26.3.useContext()--- 插件是createContext useContext ,相当于是我们的context的hooks用法,也就是让数据公共化
    这里我们创建example5.js
    import React,{useState , createContext , useContext} from 'react';

    const countContext = createContext();

    function Counter(){
    let counter = useContext(countContext);
    return(
    <div>{counter}</div>
    )
    }

    function Example5(){
    const [time,setTime] = useState(0);
    return(
    <div>
    <p>u already clicked {time} times.</p>
    <button onClick={()=>{setTime(time+1)}}>点击加一</button>
    <countContext.Provider value={time}>
    <Counter/>
    </countContext.Provider>
    </div>
    )
    }
    export default Example5;

    26.4.useReducer 经常和useContext联合使用
    我们例子是:example6.js,没有使用context
    import React,{useState , useReducer} from 'react';

    function ReducerDemo(){
    const [count , dispatch] = useReducer((state,action) => {
    switch(action){
    case 'add':
    return state+1
    case 'sub':
    return state-1
    default:
    return state
    }
    },0);
    return(
    <div>
    <p>现在的分数是:{count}分</p>
    <button onClick={()=>{dispatch('add')}}>点击加一</button>
    <button onClick={()=>{dispatch('sub')}}>点击减一</button>
    </div>
    )
    }

    export default ReducerDemo;

    然后我们建一个reducer hooks的小案例,为了让createContext和useContext和useReducer合作,叫做 course 文件夹 D:MyProject odejs26reactmyProject eact-hooks-demo08course
    然后我们先新建一个showArea.js
    里面是:
    import React from 'react';
    function ShowArea(){
    return (
    <div style = {{color:"blue"}}>字体颜色blue</div>
    )
    }
    export default ShowArea;

    然后新建Buttons.js
    import React from 'react';
    function Buttons(){
    return (
    <div>
    <button>红色</button>
    <button>黄色</button>
    </div>
    )
    }
    export default Buttons;
    然后新建一个统一他们的example7.js
    import React from 'react';
    import Bottons from './Buttons'
    import ShowArea from './showArea'
    function Example7(){
    return (
    <div>
    <ShowArea />
    <Bottons />
    </div>
    )
    }
    export default Example7;
    然后新建一个color的颜色转换reducer,因为暴露的多,所以不采用export default的写法,而是直接export const
    import React , {createContext , useReducer} from 'react';

    export const ColorContext = createContext({});

    export const UPDATE_COLOR = "UPDATE_COLOR";

    const reducer = (state , action)=>{
    switch(action.type){
    case UPDATE_COLOR:
    return action.color
    default:
    return state
    }
    }

    export const Color = props => {
    const [color , dispatch] = useReducer(reducer,'blue')
    return (
    <ColorContext.Provider value={{color,dispatch}}>
    {props.children}
    </ColorContext.Provider>
    )
    };
    然后修改example7.js
    import React from 'react';
    import Bottons from './Buttons'
    import ShowArea from './showArea'
    import { Color } from './color'
    function Example7(){
    return (
    <div>
    <Color>
    <ShowArea />
    <Bottons />
    </Color>
    </div>
    )
    }
    export default Example7;
    然后修改Buttons.js
    import React , {useContext} from 'react';
    import {ColorContext , UPDATE_COLOR} from './color'
    function Buttons(){
    const {dispatch} = useContext(ColorContext)
    return (
    <div>
    <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"red"})}}>红色</button>
    <button onClick={()=>{dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黄色</button>
    </div>
    )
    }
    export default Buttons;
    然后修改showArea.js
    import React , {useContext} from 'react';
    import { ColorContext } from './color';
    function ShowArea(){
    const {color} = useContext(ColorContext);
    return (
    <div style = {{color:color}}>字体颜色{color}</div>
    )
    }
    export default ShowArea;
    就ok拉 !

    26.5.useMemo -- 解决性能问题
    一般情况下,我们有一个父组件和子组件,当我们的状态改变时,会调用这个生命周期函数:shouldComponentUpdate
    但是当我们用hooks时【一般情况下也是如此】,我们会发现,当我们的子组件发生变化时,我们的父组件也会发生状态重新渲染【当然我说的有点主观了】大概就是下面的代码:
    import React,{useState} from 'react';
    function Example8(){
    const [junxian,setJunxian] = useState('林俊贤在接客状态');
    const [dkzhang,setDkzhang] = useState('张卫健在接客状态');
    return(
    <div>
    <button onClick={()=>{setJunxian(new Date().getTime())}}>林俊贤</button>
    <button onClick={()=>{setDkzhang(new Date().getTime()+'DK向我走来!')}}>张卫健</button>
    <ChildComponent name={junxian}>{dkzhang}</ChildComponent>
    </div>
    )
    }

    function ChildComponent({name , children}){
    function changeJunxian(){
    console.log('他来了!!!林俊贤向我走来了!!!')
    return name+',林俊贤向我走来了!'
    }
    const actionJunxian = changeJunxian(name);
    return(
    <div>
    <div>{actionJunxian}</div>
    <div>{children}</div>
    </div>
    )
    }
    export default Example8;

    这个时候,当我们刚进去的时候,控制台会有【他来了!!!林俊贤向我走来了!!!】
    但是当我们点击张卫健的按钮时,理论上,林俊贤是没有变化的,但是控制台又出现了【他来了!!!林俊贤向我走来了!!!】
    说明不管谁变化,都要重新渲染一次,这显然是不符合我们要求的
    所以,我们要用useMemo解决这个问题
    import React,{useState , useMemo} from 'react';
    function Example8(){
    const [junxian,setJunxian] = useState('林俊贤在接客状态');
    const [dkzhang,setDkzhang] = useState('张卫健在接客状态');
    return(
    <div>
    <button onClick={()=>{setJunxian(new Date().getTime())}}>林俊贤</button>
    <button onClick={()=>{setDkzhang(new Date().getTime()+'DK向我走来!')}}>张卫健</button>
    <ChildComponent name={junxian}>{dkzhang}</ChildComponent>
    </div>
    )
    }

    function ChildComponent({name , children}){
    function changeJunxian(){
    console.log('他来了!!!林俊贤向我走来了!!!')
    return name+',林俊贤向我走来了!'
    }
    // const actionJunxian = changeJunxian(name);

    //当我们调用的函数有返回值的时候,不用加{},当我们加{}的时候,就必须里面有return
    const actionJunxian = useMemo(()=>changeJunxian(name),[name]);
    return(
    <div>
    <div>{actionJunxian}</div>
    <div>{children}</div>
    </div>
    )
    }

    export default Example8;

    关于箭头函数里面要不要大括号{},当我们调用一个函数里面有return的时候,不加{},当我们调用的函数没有返回值,我们要用{},并且,我们要在{}里面return一个返回值:https://www.cnblogs.com/yangxuanxuan/p/11174334.html

    26.6.useRef()获取dom元素和保存变量
    这就是我们的example9.js
    import React,{useRef,useState,useEffect} from 'react';

    function Example9(){
    const inputEl = useRef(null);//也可以不传值
    const onButtonClick = ()=>{
    inputEl.current.value = textRef.current;
    console.log(inputEl);
    }
    const [text , setText] = useState('dkzhang');
    const textRef = useRef(null);
    useEffect(()=>{
    textRef.current = text;
    console.log(textRef)
    console.log('textRef.current:'+textRef.current)
    })
    return(
    <div>
    <input ref={inputEl} type='text' />
    <button onClick = {onButtonClick}>在input上展示文字</button>
    <br />
    <br />
    <input value={text} onChange={(e)=>{setText(e.target.value)}} />
    </div>
    )
    }
    export default Example9;

    这里要注意,我们用ref的时候,如果赋给了ref属性,那么他的结构是一个inputEl.current.value,才会出来值的
    但是如果是直接赋值的话,就直接textRef.current就可以,也就是说,他只有current一个属性,没有别的
    但是如果是赋值为ref属性,那么它的current除了value属性,还有别的很多属性,不能直接inputEl.current赋值

    26.7.自定义hooks
    自定义hooks的函数开头必须是use,这是默认的规定
    import React,{useState,useEffect,useCallback} from 'react';
    function useWinSize(){
    //初始值用一个对象
    const [size , setSize] = useState({
    document.documentElement.clientWidth,
    height:document.documentElement.clientHeight
    });

    const onResize = useCallback(()=>{
    setSize({
    document.documentElement.clientWidth,
    height:document.documentElement.clientHeight
    })
    },[])

    useEffect(()=>{
    window.addEventListener('resize',onResize)
    return ()=>{
    window.removeEventListener('resize',onResize);
    }
    },[])

    return size;
    }
    //以上就是自定义了一个hooks

    function Example10(){
    const size = useWinSize();
    return (
    <div>页面大小size:{size.width}X{size.height}</div>
    )
    }
    export default Example10;

  • 相关阅读:
    Tornado之异步authenticated
    使用Tornado和协程爬取博客园文章
    Python协程与asyncio
    Mr.Jin系统发布报告——WIN7 WIN8双系统下的学习模式系统
    Matlab:fsolve No solution found.
    Matlab R2016b下载 安装及破解教程
    第一行代码近期bug及解决
    Android Studio打包生成APK教程
    Notification通知在OPPO手机上不弹出提示?
    Android Studio如何在命令提示符中使用ADB指令
  • 原文地址:https://www.cnblogs.com/yimei/p/12941857.html
Copyright © 2020-2023  润新知