React Basics

本文介绍React基础, 例如JSX常用语法, 组件合成, 列表渲染以及动态CSS

React

Javascript

Web Development

基础

目录

npx create-react-app appName Javascript 模板

npx create-react-app appName --template typescript Typescript 模板

1. Inline Styling 行内 CSS 样式化

除了用className来修改指定元素的样式化外, JSX 本身也支持行内 CSS.

  • style为 object 类型, 因此需要{{}} 双括号解析表达式. 如果传入的为 object 则只需要一对括号.
  • 非整型值的样式化例如 px, em 则需要双引号 ""
  • CSS 属性用驼峰格式替代-

JSX 行内 CSS 通常用于实现一些动态的 CSS. 例如下面的代码用户名的颜色取决于该用户的所属组. 其他的技巧例如动态触发动画等 (也可以搭配Styled-Component).

const UserProfile = props => {
  const user_profile_style = {
    fontSize: 20,
    color: "#000",
  }

  // ADMIN组用户红色 默认黑色字体
  if (props.group === "ADMIN") {
    user_profile_style.color = "red"
  }

  return (
    <div>
      <img
        src="IMAGE-PATH"
        style={{ width: 100, height: 100, borderRadius: 50 }}
      />
      <p style={user_profile_style}>Kageyame Tobio</p>
    </div>
  )
}

export default UserProfile

2. Conditional Render 条件渲染

  1. 使用 if-else分支
const App = () => {
  const [loading, setLoading] = useState(true)

  if (loading) {
    return <p>Loading...</p>
  }
  return <div>Hello!</div>
}
  1. 使用三元运算符 根据状态渲染不同的元素
const App = () => {
  const [loading, setLoading] = useState(false)

  return <div>{loading ? <p>Loading</p> : <p>Hello</p>}</div>
}
  1. 如果只需要判断是否要显示一些元素 则可以用 &&
const App = () => {
  const [loading, setLoading] = useState(false)

  return <div>{!loading && <p>Hello</p>}</div>
}
  1. 组件通过 state 决定是否渲染 return null
const Notification = props => {
  if (!props.show) return null

  return (
    <div>
      <p>You received a message from {props.fromUser}</p>
    </div>
  )
}

3. React.Fragment

用于 group 一系列的子组件避免添加过多多余的节点, 例如 <div>

const SubjectTableHeader = () => {
  return (
    <React.Fragment>
      <th>Title</th>
      <th>Rating</th>
      <th>Rank</th>
    </React.Fragment>
  )
}

或者可直接用 shortcut <></>来替代 React.Fragment

const SubjectTableHeader = () => {
  return (
    <>
      <th>Title</th>
      <th>Rating</th>
      <th>Rank</th>
    </>
  )
}

4. Composition 组件合成

4.1 Containment 容器与children

当组件内的内容不确定时, 可通过<Component></Component>来传入具体的子内容.

更像是样式化一个容器, 然后里面所有的 children 都遵循该容器的样式化. 类似于原子设计理论中的template模板一层级.

比如下面的Modal组件, 我们有时候不确定里面的内容, 但我们确定的是所有 Modal 的内容都共享一个样式化, 例如 modal 的边界, 圆角等等...

虽然用css也可以实现样式化共享, 但创建这种容器组件我们可以传递 props或者在其基础上加入其他内容. 虽然我们不知道组件内有什么内容, 但我们知道必然有一个按钮用来返回主页. 而css单独共享样式化则需要每个div都写一个button组件

const ModalLayout = props => {
  return (
    <div className="modal-layout">
      {props.children}
      <button>Return</button>
    </div>
  )
}

const App = () => {
  return (
    <div>
      <ModalLayout>
        <h3>Submitted Successfully!</h3>
      </Modal>

      <ModalLayout>
        <h3>Enter your information</h3>
        <input type="email" placeholder="Enter email" />
      </Modal>
    </div>
  )
}

4.2 自定义的组成 props 传递组件

如果容器的样式化较复杂, 例如涉及到位置的问题. 则可以通过props来传递相关的子组件来达到组件的合成.

这个例子比较特别的一点就是props除了用来传参数同时也可以传组件.

const SubjectItemLayout = props => {
  return (
    <div>
      <h1>一些通用渲染的元素</h1>
      <div className="subject-item-layout">
        <div className="subject-item-left">{props.left}</div>
        <div className="subject-item-right">{props.right}</div>
      </div>
      {props.children}
    </div>
  )
}

const App = () => {
  return (
    <SubjectItemLayout left={<SubjectBanner />} right={<SubjectDetail />}>
      <button>Return</button>
    </SubjectItemLayout>
  )
}

5. Default Props 默认参数

设置默认props以防止组件传入的propsnull.

import {DEFAULT_USER_AVATAR} from './constant'

const ProfileCard = (props) => {
  return (
    <div>
      <img src={props.avatar} alt="user avatar" />
      <h5>{props.username}</h5>
    </div>
  )
}

ProfileCard.defaultProps = {
  avatar: DEFAULT_USER_AVATAR;
}

export default ProfileCard

6. Type Check 类型检验

文档链接

npm install prop-types

只在开发环境中 warning, 如果开发环境不是 Typescript 则可以用来测接口。

import PropTypes from "prop-types"

const Subject = props => {
  return (
    <div>
      <h3>Title: {props.title}</h3>
      <small>Category: {props.category}</small>
      <p>Rating: {props.rating}</p>
    </div>
  )
}

Subject.propTypes = {
  title: PropTypes.string.isRequired,
  category: PropTypes.oneOf(['ANIME', 'BOOK', 'MUSIC', 'GAME']) // 限制prop的值
  rating: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 限制prop的类型

}

export default Subject

7. Mapping List 列表渲染

<SubjectPreviewItem />为一个预览条目组件 (包括条目名称, 图片..用来展示条目信息).

虽然也可以直接写js表达式但单独拿出来作为一个组件可提高可读性并且可重复利用.

const subjects = [
  {
    subject_id: 51,
    name: "CLANNAD",
    name_cn: "CLANNAD",
    rating: 9.3,
  },
  // 其他条目....
]
const SubjectList = props => {
  const subjects = props.subjects
  const subjectList = subjects.map(elem => (
    <SubjectPreviewItem key={elem.id} subject={elem} />
  ))

  return <div>{subjectList}</div>
}

const App = () => {
  return (
    <>
      <h1>所有番剧</h1>
      <SubjectList subjects={subjects} />
    </>
  )
}

8. JSX Common Usage

Dot Notation 引用指定的组件

组件必须以大写字母开头. 如果用小写字母 React 则会默认为内置元素.

1. 按组件 Hierarchy 分类

例如Atomic Design中, 我们可以按分子(atoms)打包返回所有相关的组件. 然后可以用<Atoms.Component /> 来渲染指定的组件.

const Button = ({ label }) => <button>{label}</button>
const Logo = ({ imgUri }) => <img src={imgUri} />

const Atoms = {
  Button: Button,
  Logo: Logo,
}

App.js

const App = () => {
  return (
    <>
      <Atoms.Button label="Submit" />
    </>
  )
}

2. 按组件的 Category 分类

利用 JS []根据提供的key来渲染指定的组件.

const storyComponents = {
  photo: PhotoStory,
  video: VideoStory,
}

const Story = ({ storyType, stories }) => {
  const Component = storyComponents[storyType]
  return <Component stories={stories} />
}

... & Deconstruct Props

1. 向下传递props为整个 object

直接传递props尽可能避免再嵌一层key. 例如下面如果用data={elem}, 子组件中又还要先拆一层props.data

const SUBJECTS = [
  { id: 1, name: "Haikyuu" },
  { id: 2, name: "CLANNAD" },
  { id: 3, name: "Fate Stay Night" },
]
const App = () => {
  const subjectList = SUBJECTS.map(elem => <Subject key={elem.id} {...elem} />)

  return (
    <>
      <h1>All Subjects</h1>
      {subjectList}
    </>
  )
}

2. 只传递部分 Props (Deconstruct)

假设我们只需要其中部分的信息, 可以 deconstruct props 只传递我们需要的

const subjectList = SUBJECTS.map(elem => {
  // 虽然我们的假数据只有id和name (lol)
  const props = { id: elem.id, name: elem.name }
  return <Card key={props.id} {...props} />
})

3. 子组件 Deconstruct 传入的 Props

同第二点, 我们或者可以在子组件里 deconstruct 需要的 props

const Subject = props => {
  // other则为剩下的
  const { name, name_cn, rating, ...other } = props

  return (
    <>
      <h1>{name}</h1>
      <h3>{name_cn}</h3>
      <small>{rating}</small>
    </>
  )
}