React一学就会(4): 强化练习二

书接上回,是不是感觉已经有点入门了。不过别急,码哥我准备了很多干货,等我们把这些基本几个章节的学完,码哥带着你一起装逼一起飞。我不大可能只是带着你们入门,那不是我的风格。码哥会教你如何开发一个完整中后台。前端后端如何匹配,什么安全什么策略都会教给你。如果你还有其它兴趣,我还可以教你许多网络知识,Android开发、IOS开发,Mac开发、
c#开发等等。总之,一年的时间你肯定学不完。根本学不完。

条件渲染

一句话,根据某种条件做出不同的渲染元素。所谓的条件就是各种判断,if 也好 三目运算符也好。都行
比如,下面的两个用户组件:
为了美观,下面所有的示例中我统一用MUI的样式组件,关于MUI的安装我在第一节课就已经讲过了,如果你是从第一节学过来的,应该没什么问题。

// Users.jsx
import Typography from '@mui/material/Typography';

function UserGreeting(props) {
    return <Typography variant="h1" gutterBottom> 欢迎回来!</Typography>;
}

function GuestGreeting(props) {
    return <Typography variant="h1" gutterBottom>请登录.</Typography>;
}

export { UserGreeting, GuestGreeting };

现在我们要创建一个新的组件,这个组件的功能是根据用户登录的条件来显示其中一个组件。 我们来创建Greeging组件如下:

//Greeting.jsx
import { UserGreeting, GuestGreeting } from "./Users";

export default function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    if (isLoggedIn) {
        return <UserGreeting />;
    }
    return <GuestGreeting />;
}

你看,这个最基本的条件渲染组件就完成了。很简单是不是,当然,我们还可以把条件弄的复杂一点:

元素变量

所谓元素变量就是把一个组件元素存储在一个变量中,渲染时直接渲染这个变量就可以了
比如有下面两个组件,一个是登录组件 LoginButton,一个是登出组件 LogoutButton

// logButtons.jsx
import Button from "@mui/material/Button";

function LoginButton(props) {
    return (
        <Button variant="outlined" onClick={props.onClick}>
            登录
        </Button>
    );
}

function LogoutButton(props) {
    return (
        <Button variant="outlined" onClick={props.onClick}>
            退出
        </Button>
    );
}

export { LoginButton, LogoutButton };

接下来我们创建一个有状态的组件,根据上面两个按钮点击的结果显示不同的界面。

// LoginControl.jsx
import React from 'react';
import { LoginButton, LogoutButton } from './LogButtons';
import Greeting from './Greeting';
import Box from '@mui/material/Box';

class LoginControl extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isLoggedIn: false };

        //在构造函数中我们采用先绑定this的方法与事件绑定
        this.handleLoginClick = this.handleLoginClick.bind(this);
        this.handleLogoutClick = this.handleLogoutClick.bind(this);
    }

    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 (
            <Box>
                <Greeting isLoggedIn={isLoggedIn} />
                {button}
            </Box>
        );
    }
}

export default LoginControl;

至于为什么我这里不用函数组件的方式写这个 LoginControl 组件呢,因为我们还有很多Hook没有学习。当我们学习了所有的相关知识后,你就可以想怎么写就怎么写了。
上面的逻辑已经很清晰了,不用多讲什么了吧。
下面我们在App.jsx中调用这个组件就OK了,是不是很Nice,有点小激动不。

//App.jsx
import './App.css'
import "./styles.css";
// import Profile from './Profile'
// import ButtonClickTest from './buttonClickTest';
// import MyCompButton from './Test03/MyButtons';
// import Counter from './Test03/ClassComponentTest';
// import Clock from './Test03/Clock';
// import ClockFunc from './Test03/ClockFunc';
import LoginControl from './Test04/LoginControl';

function App() {
  return (
    <LoginControl />
  )
}

export default App
关于 && 及 || 的渲染应用示例。

先看下面的示例

// Mailbox.jsx
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";

export default function Mailbox(props) {
    const unreadMessages = props.unreadMessages;
    return (
        <Box>
            <Typography variant="h1">你好!</Typography>
            {
                unreadMessages.length > 0 &&
                <Typography variant="h2">
                    你有 {unreadMessages.length} 条未读消息。
                </Typography>
            }
        </Box>
    );
}

这个时候如果我们直接在App.jsx中调用这个组件,什么也不会显示。但我们给 unreadMessages 传递两条信息就不一样了

//App.jsx
import './App.css'
import "./styles.css";
// import Profile from './Profile'
// import ButtonClickTest from './buttonClickTest';
// import MyCompButton from './Test03/MyButtons';
// import Counter from './Test03/ClassComponentTest';
// import Clock from './Test03/Clock';
// import ClockFunc from './Test03/ClockFunc';
// import LoginControl from './Test04/LoginControl';
import Mailbox from './Test04/Mailbox';

function App() {
  return (
    <Mailbox unreadMessages={["message1", "message2"]} />
  )
}

export default App

是不是有点意思。
其实 条件 && 组件 的意思是 当条件为真时才渲染组件, 这就是 && 的用法。类似的 || 的用法是这样的:
变量 || 组件 表示当变量不为null时,就显示 变量,当变量为 null 时,就显示 组件。我们也常常用 || 变量提供一个备选值。

不有三目运算符的用法:
我们把上面 组件 Greeting 修改一下:

import { UserGreeting, GuestGreeting } from "./Users";

export default function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;
    return (
        <Box>
        {
            isLoggedIn
            ? <UserGreeting /> 
            : <GuestGreeting />
        }
        </Box>
    )
}

这样更直观一点。

阻止组件渲染

有时候我们希望在某种情况下我们的组件不要渲染出来。我们在 html 中用 css 可以设置隐藏组件,但它还是在文档流中,只是被隐藏了。但在我们的React中可以让它真正的消失,要达到这种效果,我们只要在组件中返回 null 即可。

//Page.jsx
import { Component } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';

function WarningBanner(props) {
    return (
        props.warn
            ? <Box className="warning"> 警告! </Box>
            : null
    );
}

class Page extends Component {
    constructor(props) {
        super(props);
        this.state = { showWarning: true };
        this.handleToggleClick = this.handleToggleClick.bind(this);
    }

    handleToggleClick() {
        this.setState(state => ({
            showWarning: !state.showWarning
        }));
    }

    render() {
        return (
            <Box>
                <WarningBanner warn={this.state.showWarning} />
                <Button variant="contained" onClick={this.handleToggleClick}>
                    {this.state.showWarning ? '隐藏' : '显示'}
                </Button>
            </Box>
        );
    }
}

export default Page;

上面我们定义了两个组件 WarningBannerPage, Page组件中根据按钮点击后的的状态显示 WarningBanner
在 App.jsx中调用一下查看结果

import './App.css'
import "./styles.css";
// import Profile from './Profile'
// import ButtonClickTest from './buttonClickTest';
// import MyCompButton from './Test03/MyButtons';
// import Counter from './Test03/ClassComponentTest';
// import Clock from './Test03/Clock';
// import ClockFunc from './Test03/ClockFunc';
// import LoginControl from './Test04/LoginControl';
// import Mailbox from './Test04/Mailbox';
import Page from './Test04/Page';

function App() {
  return (
    <Page />
  )
}

export default App

列表

我们来看看 JavaScript 中的数组遍历, 在控制台查看结果。

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

在 React 中有时显示多个元素时也是用这个方法,如

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li> { number } </li>
);
基本列表

如下示例:

// NumberList.jsx
export default function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    <ul> { listItems } </ul>
  );
}

如果用的是VSCode,那么这时候应该有一个语法警告提示,我们先不管它。
我们在App.jsx中调用并传入props参数

import './App.css'
import "./styles.css";
import NumberList from './Test04/Numbers';

function App() {
  return (
    <NumberList numbers = {[1, 2, 3, 4, 5, 6]} />
  )
}

export default App

此时运行,你会发现在控制台会有一条警告提示信息:
在这里插入图片描述

意思是列表元素少了 key 这个属性。React要求,所有列表元素都必须要用 key 属性,它可以帮助 React 识别哪些项目已更改、添加或删除。应该为数组中的元素提供 key,以使元素具有稳定的标识, 我们修改如下:

const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

const todoItems = todos.map((todo, index) =>
  <li key={index}>
    {todo.text}
  </li>
);

确保每个列表项的key为唯一即可。 还有一点要注意的是我们不应该把 key 直接写入组件,比如下面的做法是错误的。

function ListItem(props) {
  const value = props.value;
  return (
    // 错误,这里直接把key写到元素上了,li 原始元素并没有这个属性。
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 错误。我们应该在这里为React组件指定key
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

正确的做法是这样的:

function ListItem(props) {
  // 相当OK, 这里不需要指定 key, 
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 完全OK, 在这里我们为React组件指定了Key。 key是React内部使用,跟我们如何定义组件并没有多大的关联。数组列表我们额外加上就行了。
    <ListItem key={number.toString()} value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

还有一点要注意,这个key 只要在同级之前是唯一的就行。如果列表内嵌套列表,子列表的 key 和外层的key即使相同也不冲突。像下面的示例

// Blog.jsx
export default function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

调用并运行它,我们会发现一切正常

import './App.css'
import "./styles.css";
import Blog from './Test04/Blog';

const posts = [
  { id: 1, title: 'Hello World', content: 'Welcome to learning React!' },
  { id: 2, title: 'Installation', content: 'You can install React from npm.' }
];

function App() {
  return (
    <Blog posts={posts} />
  )
}

export default App

当前也可以直接在JSX中嵌入map

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}

是不是感觉清爽多了。

你可能感兴趣的:(前端react技术积累,react.js,前端,前端框架)