基础
目录
- Inline Styling 行内 CSS 样式化
- Conditional Render 条件渲染
- React.Fragment
- Composition 组件合成
- Default Props 默认参数
- Type Check with JSX
- Mapping List 列表渲染
- JSX
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 条件渲染
- 使用
if-else
分支
const App = () => {
const [loading, setLoading] = useState(true)
if (loading) {
return <p>Loading...</p>
}
return <div>Hello!</div>
}
- 使用三元运算符 根据状态渲染不同的元素
const App = () => {
const [loading, setLoading] = useState(false)
return <div>{loading ? <p>Loading</p> : <p>Hello</p>}</div>
}
- 如果只需要判断是否要显示一些元素 则可以用
&&
const App = () => {
const [loading, setLoading] = useState(false)
return <div>{!loading && <p>Hello</p>}</div>
}
- 组件通过 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
以防止组件传入的props
为null
.
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>
</>
)
}