1. 组件
1.1. 函数式
定义一个组件最简单的方式是使用JavaScript函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
该函数是一个有效的React组件,它接收一个单一的“props”对象并返回了一个React元素。我们之所以称这种类型的组件为函数定义组件,是因为从字面上来看,它就是一个JavaScript函数。
1.2. es6形式
也可以使用 ES6 class来定义一个组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
1.3. props
当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。
例如,这段代码会在页面上渲染出”Hello,Sara”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
1.4. 高阶组件
https://react.docschina.org/docs/higher-order-components.html
高阶组件就是一个函数,它接收一个组件,然后返回一个组件,如果我们需要给一些组件加一些特定的参数活方法, 则可以用到高阶组件,来看一个例子:
import React from 'react'
import { AppContext } from './AppContext'
const withContext = (Component) => {
return (props) => (
<AppContext.Consumer>
{({ state, actions }) => {
return <Component {...props} data={state} actions={actions} />
}}
</AppContext.Consumer>
)
}
export default withContext
create.js
export default withContext(CreatePage)
1.5 展示型组件

来看一个例子:
// 父组件
modifyItem = (item) => {
this.props.history.push(`/edit/${item.id}`)
}
<PriceList
items={itemsWithCategory}
onModifyItem={this.modifyItem}
/>
// 子组件
const PriceList = ({ items, onModifyItem }) => {
return (
<ul className="list-group list-group-flush">
{
items.map((item) => (
<li className="list-group-item d-flex
justify-content-between align-items-center"
key={item.id}
>
<a className="col-1"
role="button"
onClick={(event) => {event.preventDefault(); onModifyItem(item)}}
>
</li>
))
}
</ul>
)
}
1.6 容器组件

2. 生命周期
这是官网的生命周期图例:

componentWillMount()
组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。
render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。此时就不能更改state了。
componentDidMount()
组件渲染之后调用,只调用一次。
componentWillReceiveProps(nextProps)
组件初始化时不调用,组件接受新的props时调用。
shouldComponentUpdate(nextProps, nextState)
react性能优化非常重要的一环。组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会生成相同的dom树,这样就不需要创造新的dom树和旧的dom树进行diff算法对比,节省大量性能,尤其是在dom结构复杂的时候
componentWillUpdata(nextProps, nextState)
组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state
render()
组件渲染
componentDidUpdate()
组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。
componentWillUnmount()
组件将要卸载时调用,一些事件监听和定时器需要在此时清除。
3. state
状态的更新过程是异步的,React 可以将多个setState() 调用合并成一个调用来提高性能。
因为 this.props 和 this.state 可能是异步更新的,所以不应该依靠它们的值来计算下一个状态。
例如,以下代码的结果可能是错误的:
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用第二种形式的 setState() 来接受一个函数而不是一个对象。 该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props做为第二个参数:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
4. 条件渲染
4.1 元素变量
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
4.2 与运算符
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
4.3 三目运算符
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
4.4 阻止组件渲染
在极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。让 render 方法返回 null 而不是它的渲染结果即可实现。
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
5. 列表循环
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
// 或
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。
如果你提取出一个ListItem组件,你应该把key保存在数组中的这个<ListItem />元素上,而不是放在ListItem组件中的<li>元素上。
function ListItem(props) {
// 对啦!这里不需要指定key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 又对啦!key应该在数组的上下文中被指定
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
6. 组合继承
6.1 包含关系
一些组件不能提前知道它们的子组件是什么。这对于 Sidebar 或 Dialog 这类通用容器尤其常见。
我们建议这些组件使用 children 属性将子元素直接传递到输出。
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
有时你可能需要在组件中有多个入口,这种情况下你可以使用自己约定的属性而不是 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 />
} />
);
}
6.2 特殊实例
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
7. 实用技巧
可以覆盖展开后的title
let item = {
"title": "标题",
"content": "内容"
}
return {...item, title: "更新后的标题"}
将上一步数据返回后可以实现链式调用
items.filter(item => item.category.type === type).forEach((item) => {
if (categoryMap[item.cid]) {
categoryMap[item.cid].value += (item.price * 1)
categoryMap[item.cid].items = [...categoryMap[item.cid].items, item.id]
} else {
categoryMap[item.cid] = {
category: item.category,
value: item.price * 1,
items: [item.id]
}
}
})
函数动态传参,[]将参数变为变量
handleOpen = (type)=>{
this.setState({
[type]:true
})
}
模拟数据:json-server
fetch缺点:
concurrently 可以运行多个npm命令
react中package.json中也可以开启代理:
"proxy": "http://localhost:3004"
8. 动画与公共样式
8.1 react-transition-group
Transiton是更底层的,CSSTransition实现不了的,可以用Transiton,TransitionGroup可以实现多个dom元素动画
使用方法:
import { CSSTransition } from 'react-transition-group'
<CSSTransition
in={focused}
timeout={200}
classNames="slide"
>
<div></div>
</CSSTransition>
8.2 styled-components
如果我们想把css以js形式来写,可以使用styled-components,首先我们可以定义全局样式
// style.js
import { createGlobalStyle } from 'styled-components';
export const GlobalStyle = createGlobalStyle`
body{}
`
// app.js
import { GlobalStyle } from './style';
import { IconfontStyle } from './statics/iconfont/iconfont.js'; // 引入字体包
class App extends Component {
render() {
return (
<Provider store={store}>
<GlobalStyle />
<IconfontStyle />
</Provider>
)
}
}
// 组件样式引入
// style.js
import styled from 'styled-components';
export const HeaderWrapper = styled.div`
position: relative;
height: 56px;
border-bottom: 1px solid #f0f0f0;
z-index: 1;
`;
// index.js
import {
HeaderWrapper
} from './style';
<HeaderWrapper></HeaderWrapper>
8.3 沙箱css
首先我们需要配置webpack
在config目录下的webpack.config.js中,开启模块化(配置stylus,less也在这里配置)
react中配置less: https://www.jianshu.com/p/dbc8e0e80de5
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: true, // 开启模块化
getLocalIdent: getCSSModuleLocalIdent,
})
}
用法:
import styles from './index.module.less'
<div className={styles.detail}>
</div>
注意less文件要带.module.less的后缀
8.4 字体图标
推荐一个字体图标库:
[https://ionicons.com/](https://ionicons.com/)
复制.eot,.svg,.ttf,.woff文件包
网友评论