React条件列表渲染及组件生命周期(三)

164 阅读3分钟

一、条件和列表渲染

条件渲染

class App extends React.Component {
  	state = {
            isLogin: true,
        };
   
    render() {
        const { isLogin } = this.state;

        // 1.方案一:通过if判断: 逻辑代码非常多的情况
        let welcome = null;
        let btnText = null;
        if (isLogin) {
            welcome = <h2>欢迎回来~</h2>;
            btnText = "退出";
        } else {
            welcome = <h2>请先登录~</h2>;
            btnText = "登录";
        }

        return (
            <div>
                {welcome}
                {/* 2.方案二: 三元运算符 */}
                <button onClick={(e) => this.loginClick()}>
                    {isLogin ? "退出" : "登录"}
                </button>
                <hr />
                <h2>{isLogin ? "你好啊, yunmu" : null}</h2>

                {/* 3.方案三: 逻辑与&& */}
                {/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了 */}
                <h2>{isLogin && "你好啊, yunmu"}</h2>
                {isLogin && <h2>你好啊, yunmu</h2>}
            </div>
        );
    }

    loginClick() {
        this.setState({
            isLogin: !this.state.isLogin,
        });
    }
}

条件渲染-v-show效果

class App extends React.Component {
    state = {
        isLogin: true,
    };

    render() {
        const { isLogin } = this.state;
        const titleDisplayValue = isLogin ? "block" : "none";
        return (
            <div>
                <button onClick={(e) => this.loginClick()}>
                    {isLogin ? "退出" : "登录"}
                </button>
                <h2 style={{ display: titleDisplayValue }}>你好啊, yunmu</h2>
            </div>
        );
    }

    loginClick() {
        this.setState({
            isLogin: !this.state.isLogin,
        });
    }
}

列表渲染

class App extends React.Component {
    this.state = {
        names: ["abc", "cba", "nba", "mba", "dna"],
        numbers: [110, 123, 50, 32, 55, 10, 8, 333],
    };


    render() {
        return (
            <div>
                <h2>名字列表</h2>
                <ul>
                    {this.state.names.map((item) => {
                        return <li>{item}</li>;
                    })}
                </ul>

                <h2>数字列表(过滤1)</h2>
                <ul>
                    {this.state.numbers.filter((item) => {
                        return item >= 50;
                    }).map((item) => {
                        return <li>{item}</li>;
                    })}
                </ul>

                <h2>数字列表(过滤2)</h2>
                <ul>
                    {this.state.numbers.filter((item) => item >= 50)
                        .map((item) => (<li>{item}</li>))}
                </ul>

                <h2>数字列表(截取)</h2>
                <ul>
                    {this.state.numbers.slice(0, 4).map((item) => {
                        return <li>{item}</li>;
                    })}
                </ul>
            </div>
        );
    }
}

key是虚拟DOM对象的标识,,使用列表渲染的时候注意加key可以在更新显示时提升性能

最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

最好不要使用index作为key,因为如果对数据进行逆序添加、逆序删除等破坏顺序操作会产生没有必要的真实DOM更新

仅用于渲染列表用于展示,使用index作为key是没有问题的

class Person extends React.Component {
    state = {
        persons: [
            { id: 1, name: "小张", age: 18 },
            { id: 2, name: "小李", age: 19 },
        ],
    };

    render() {
        return (
            <div>
                <h3>使用id(数据的唯一标识)作为key</h3>
                <ul>
                    {this.state.persons.map((personObj) => {
                        return (
                            <li key={personObj.id}>
                                {personObj.name}---{personObj.age}
                                <input type="text" />
                            </li>
                        );
                    })}
                </ul>
            </div>
        );
    }
}

购物车案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <style>
      table {
        border: 1px solid #eee;
        border-collapse: collapse;
      }

      th,
      td {
        border: 1px solid #eee;
        padding: 10px 16px;
        text-align: center;
      }

      th {
        background-color: #ccc;
      }

      .count{
        margin: 0 5px;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>

    <script src="../react/react.development.js"></script>
    <script src="../react/react-dom.development.js"></script>
    <script src="../react/babel.min.js"></script>

    <script src="./format-utils.js"></script>
    <script type="text/babel">
      class App extends React.Component {
        state = {
          books: [
            {
              id: 1,
              name: "《算法导论》",
              date: "2006-9",
              price: 85.0,
              count: 2,
            },
            {
              id: 2,
              name: "《UNIX编程艺术》",
              date: "2006-2",
              price: 59.0,
              count: 1,
            },
            {
              id: 3,
              name: "《编程珠玑》",
              date: "2008-10",
              price: 39.0,
              count: 1,
            },
            {
              id: 4,
              name: "《代码大全》",
              date: "2006-3",
              price: 128.0,
              count: 1,
            },
          ],
        };

        renderBook() {
          return (
            <div>
              <table>
                <thead>
                  <tr>
                    <th></th>
                    <th>书籍名称</th>
                    <th>出版日期</th>
                    <th>价格</th>
                    <th>购买数量</th>
                    <th>操作</th>
                  </tr>
                </thead>
                <tbody>
                  {this.state.books.map((item, index) => {
                    return (
                      <tr>
                        <td>{index + 1}</td>
                        <td>{item.name}</td>
                        <td>{item.date}</td>
                        <td>{item.price}</td>
                        <td>
                          <button onClick={() => this.changeCount(index, -1)}>
                            -
                          </button>
                          <span className="count">{item.count}</span>
                          <button onClick={() => this.changeCount(index, 1)}>
                            +
                          </button>
                        </td>
                        <td onClick={(e) => this.removeBook(e, index)}>
                          移除
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>

              <h2>总价格{this.totoalPrice()}</h2>
            </div>
          );
        }

        renderEmptyBook() {
          return <h2>购物车已被清空</h2>;
        }

        render() {
          return this.state.books.length
            ? this.renderBook()
            : this.renderEmptyBook();
        }

        totoalPrice() {
          const total = this.state.books.reduce((preVal, item) => {
            return preVal + item.count * item.price;
          }, 0);

          return formatPrice(total);
        }

        removeBook(e, index) {
          const changeBook = this.state.books.filter(
            (item, indey) => index !== indey
          );

          this.setState({
            books: changeBook,
          });
        }

        changeCount(index, count) {
          const newBookArr = [...this.state.books];

          newBookArr[index].count += count;

          if (newBookArr[index].count <= 0) newBookArr[index].count = 0;

          this.setState({
            books: newBookArr,
          });
        }
      }

      ReactDOM.render(<App />, document.getElementById("app"));
    </script>
  </body>
</html>

二、生命周期

React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用

我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作

页面加载时使文字透明度不断递减恢复

component    生命周期

//生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
class Life extends React.Component{

    state = {opacity:1}

    death = () => {
        //卸载组件
        ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }

    //组件挂完毕
    componentDidMount(){
        console.log('componentDidMount');
        this.timer = setInterval(() => {
            //获取原状态
            let {opacity} = this.state
            //减小0.1
            opacity -= 0.1
            if(opacity <= 0) opacity = 1
            //设置新的透明度
            this.setState({opacity})
        }, 200);
    }

    //组件将要卸载
    componentWillUnmount(){
        //清除定时器
        clearInterval(this.timer)
    }

    //初始化渲染、状态更新之后
    render(){
        console.log('render');
        return(
            <div>
                <h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
                <button onClick={this.death}>不活了</button>
            </div>
        )
    }
}

react生命周期(新)

React 生命周期主要包括三个阶段:初始化阶段,更新阶段,销毁阶段

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染

    1. constructor()
    2. getDerivedStateFromProps()
    3. render()
    4. componentDidMount() 常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

    1. getDerivedStateFromProps()
    2. shouldComponentUpdate()
    3. render()
    4. getSnapshotBeforeUpdate()
    5. componentDidUpdate()
  3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

    1. componentWillUnmount() 常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
class Count extends React.Component {					
    constructor(props) {
        console.log("Count---constructor");
        super(props);
        //初始化状态
        this.state = { count: 0 };
    }

    //加1按钮的回调
    add = () => {
        //获取原状态
        const { count } = this.state;
        //更新状态
        this.setState({ count: count + 1 });
    };

    //卸载组件按钮的回调
    death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById("test"));
    };

    //强制更新按钮的回调
    force = () => {
        this.forceUpdate();
    };

    // 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
    static getDerivedStateFromProps(props, state) {
        console.log("getDerivedStateFromProps", props, state);
        return null;
    }

    //在更新之前获取快照
    getSnapshotBeforeUpdate() {
        console.log("getSnapshotBeforeUpdate");
        return "yunmu";
    }

    //组件挂载完毕的钩子
    componentDidMount() {
        console.log("Count---componentDidMount");
    }

    //组件将要卸载的钩子
    componentWillUnmount() {
        console.log("Count---componentWillUnmount");
    }

    //控制组件更新的“阀门”
    shouldComponentUpdate() {
        console.log("Count---shouldComponentUpdate");
        return true;
    }

    //组件更新完毕的钩子
    componentDidUpdate(preProps, preState, snapshotValue) {
        console.log(
            "Count---componentDidUpdate",
            preProps,
            preState,
            snapshotValue
        );
    }

    render() {
        console.log("Count---render");
        const { count } = this.state;
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <button onClick={this.add}>点我+1</button>
                <button onClick={this.death}>卸载组件</button>
                <button onClick={this.force}>
                    不更改任何状态中的数据,强制更新一下
                </button>
            </div>
        );
    }
}

getSnapshotBeforeUpdate的使用场景

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>4_getSnapShotBeforeUpdate的使用场景</title>
    <style>
      .list {
        width: 200px;
        height: 150px;
        background-color: skyblue;
        overflow: auto;
      }
      .news {
        height: 30px;
      }
    </style>
  </head>
  <body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>

    <!-- 引入react核心库 -->
    <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

    <script type="text/babel">
      class NewsList extends React.Component {
        state = { newsArr: [] };

        componentDidMount() {
          setInterval(() => {
            //获取原状态
            const { newsArr } = this.state;
            //模拟一条新闻
            const news = "新闻" + (newsArr.length + 1);
            //更新状态
            this.setState({ newsArr: [news, ...newsArr] });
          }, 1000);
        }

        getSnapshotBeforeUpdate() {
          return this.refs.list.scrollHeight;
        }

        componentDidUpdate(preProps, preState, SnapshotHeight) {
          this.refs.list.scrollTop += this.refs.list.scrollHeight - SnapshotHeight;
        }

        render() {
          return (
            <div className="list" ref="list">
              {this.state.newsArr.map((n, index) => {
                return (
                  <div key={index} className="news">
                    {n}
                  </div>
                );
              })}
            </div>
          );
        }
      }
      ReactDOM.render(<NewsList />, document.getElementById("test"));
    </script>
  </body>
</html>