1.jsx:执行时会调用React.createElement()
const name = 'Josh Perez';
const element = Hello, {name}
;
ReactDOM.render(
element,
document.getElementById('root')
);
2. 列表渲染:map方法,key一般为id,实在没有就使用`index-${index}`,需为字符串
{numbers.map((number) =>
)}
3.组件:分为函数组件和class组件
// 函数组件
function btn(props) {
return
}
// class组件
class btn extends React.Component{
render() {
return
}
}
4.state和props:单向数据流,从父到子
setState可能会是异步的,解决方法可以让 setState()
接收一个函数而不是一个对象
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
5.生命周期:
render:为纯函数,如果shouldComponentUpdate()
返回 false则不会调用render
constructor:构造函数,直接给state赋值,在外部的话用this.setState()
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
componentDidMount:会在组件挂载后(插入 DOM 树中)立即调用,常用与api请求
componentDidUpdate:会在更新后会被立即调用,可以进行dom操作,如果比较更新前后的prop在这也可以进行api请求或者setState,但是切记要if进行比较,如果 shouldComponentUpdate()
返回 false,则不会调用 componentDidUpdate()
componentDidUpdate(prevProps,prevState) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
shouldComponentUpdate:
shouldComponentUpdate(nextProps, nextState) {
//可以在此比较控制返回true会继续渲染,返回false则不会渲染,会跳过render和conponentDidUpdate
// 在此处只应该进行浅层比较
}
componentWillUnmount:在组件销毁前调用,可以在这里清除定时器、取消订阅
setState:延迟批量更新,用callback使用最新的state或者在promise里使用
setState(updater, [callback])回调函数中的state是更新后的,可以直接使用
6.事件
命名:小驼峰式,例如onClick
阻止默认行为:不是return false,而是e.preventDefault
绑定this:
// 在构造函数中绑定
this.handleClick = this.handleClick.bind(this);
// public class fields 语法,建议使用
handleClick = () => {
console.log('this is:', this);
}
// 箭头函数,调用时和上面两种不同
handleClick() {
console.log('this is:', this);
}
参数传递:
7.条件渲染:if、&&、?:(三目运算符)
8.表单
受控组件:可以控制value,大部分都是并且建议使用
非受控组件:例如文件input标签,一般用ref获取值和控制dom
9.‘插槽’,可以将任何东西作为props传递下去
function FancyBorder(props) {
return (
{props.children}
);
}
10.父子组件和爷孙组件传值
11.refs转发:普通的ref在HOC中不会透传,需要使用React.forwardRef
API
const FancyButton = React.forwardRef((props, ref) => (
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
Click me! ;
12.高阶组件:是参数为组件,返回值为新组件的函数
不要再render里面使用HOC
13.JSX
用户定义的组件必须以大写开头
// 当props.messages为空数组时,length为0仍然会渲染后面的
{props.messages.length &&
}
可以该成props.message.length>0 &&....
14.prop-types库:检查prop的类型
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
Hello, {this.props.name}
);
}
}
Greeting.propTypes = {
name: PropTypes.string, // 类型
children: PropTypes.element.isRequired // 必须传
};
// 指定 props 的默认值:
Greeting.defaultProps = {
name: 'Stranger'
};
15.API
React.Component:创建类组件
React.PureComponent:比其多了一个shouldcomponentupdate,可以实现对prop和state的浅层对比,如果对象中包含复杂的数据结构,则可能会出错,并且这个钩子会跳过所有子组件的prop更新
React.memo:类似于前者,也只会做浅层对比,但是只适用于函数组件,仅检查props变更,若不变则React 将跳过渲染组件的操作并直接复用最近一次渲染的结果,如果函数组件中包含有useState或useContent,当 context 发生变化时,它仍会重新渲染,该函数可以接受第二个参数,为前后prop对比函数,返回的结果同shouldcomponentupdate相反,prop相同时返回true
React.createElement:参数类型可以是标签、react组件、 React fragment 类型,jsx是使用了这个函数的语法糖
React.createElement(
type,
[props],
[...children]
)
React.Fragment:不额外创建dom,却可以返回多个元素,简写<>>
render() {
return (
Some text.
A heading
);
}
React.createRef:创建一个能够通过 ref 属性附加到 React 元素
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return ;
}
}
React.forwardRef:会创建一个React组件,组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中接受渲染函数作为参数。React 将使用 props
和 ref
作为参数来调用此函数。此函数应返回 React 节点。
const FancyButton = React.forwardRef((props, ref) => (
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
Click me! ;
React.lazy:可以定义一个动态加载的组件,
const SomeComponent = React.lazy(() => import('./SomeComponent'));
16.DOM:小驼峰写法
类是className,style
在 React 应用中多用于在渲染过程中添加动态计算的样式,style接受一个采用小驼峰命名属性的 JavaScript 对象
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return Hello World!;
}
17.HOOK(重点)
①useState:const [count, setCount] = useState(0);之后在函数组件里直接使用,类似于class组件的state,count为变量名,而setCount为更新变量的函数,不像 class 中的 this.setState
,更新 state 变量总是替换它而不是合并它
setCount(prevCount => prevCount - 1)} // 传入一个箭头函数可以使用之前最新的数据
如果初始化的值需要复杂计算,则可以放入一个箭头函数,return一个值,这个初始化也是只有初始渲染时调用一次
②useEffect:componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合,依次以声明顺序调用,在重新渲染前会清除上一个effect,将在每轮渲染结束后执行
不需要清除的:
useEffect(() => {
document.title = `You clicked ${count} times`;
});
需要清除的:会在组件卸载的时候清除
useEffect(() => {
........
return () => { // 清除
......
}
});
需要跳过effect的:传入一个依赖数组,类似于在 componentDidUpdate
中添加对 prevProps
或 prevState
的比较逻辑
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
注:请确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量 导致无限循环,如果只想运行一次可以传入一个空数组
使用规则:只在最顶层使用,不要再循环、条件、嵌套函数中使用,如果要使用条件语句就把if放入effect中使用,只在react函数组件使用
③useContext:相当于 class 组件中的 static contextType = MyContext,useContext()中的参数只能是context对象本身,并且需要上级组件
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
);
}
function Toolbar(props) {
return (
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
);
}
④useReducer:const [state, dispatch] = useReducer(reducer, initialArg, init);接收(state, action) => newState
的 reducer
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
>
);
}
这里的dispatch可以从useEffect中省略,因为其实 稳定不会改变的
初始化:简单的指定初始化
const [state, dispatch] = useReducer(
reducer,
{count: initialCount} );
⑤useCallback:返回一个 memoized 回调函数,useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
const memoizedCallback = useCallback( // 依赖项数组不会作为参数传给回调函数
() => {
doSomething(a, b);
},
[a, b],
);
⑥useMemo: 返回一个 memoized 值
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
⑦useRef :useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数,常用于命令式访问组件,在一个生命周期内不会改变
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
>
);
}
⑧useLayoutEffect:
与useEffect
相同,但会在所有DOM变更后同步调用 effect,可以用它来读取 DOM 布局并同步触发重渲染
⑨useDispatch(产生dispatch)、useSelector
(获取redux里面的一些值)
自定义hook:一般以use开头,本质也是函数
1.原则:单一数据源,只有一个store;state只可读,只能通过action来改变;使用纯函数的reducer来执行修改
2.action:一般类型为{type: 'xxxx, aaa:'xxxxxxxx'},其中type一般为大写常量,aaa为携带的数据
action创建函数:返回一个action
import { Dispatch } from 'redux';
import { DriverSpreadActionType } from '../reducers/driverSpread';
import { serviceStatusList, queryCity, distributeMaps } from '../api';
export function requestServiceStatusList() {
return async (dispath: Dispatch) => {
const res: any = await serviceStatusList();
if (res && res.re) {
dispath({
type: DriverSpreadActionType.GET_SERVICESTATUS_LIST_DATA,
payload: {
serviceStatusList: res.re.statusList,
},
});
}
};
}
3. reducer:接收一个旧的state和action,返回一个新的state,不要再里面进行api等操作,保持纯净
export default function (state: any = initState, action: any) {
// 一般在函数参数中直接用es6传入初始state
switch (action.type) {
case ActionType.SEARCH_CAR_REPORT_LIST_DATA:
if (action.payload) {
return Object.assign({}, state, action.payload);// 此处也可以用...对象展开运算符
}
......
}
合并多个reducer:
import { combineReducers } from 'redux'
const todoApp = combineReducers({
visibilityFilter,
todos
})
创建store:
import { createStore } from 'redux'
import todoApp from './reducers' // 此处的为合并之后的reducer
let store = createStore(todoApp)
react-redux:一般用于class组件 ,用容器组件将redux变成组件的props之后传入展示组件,容器组件还能分发action
import { connect } from 'react-redux';
import { addTabPage, loadEnum } from '../../../../actions/home';
// 调用时就当成props使用
const {
pageNo,
pageSize,
orderListRe,
showOperationRecordFlag,
enums,
orderOptRecordRe,
} = this.props;
const mapStateToProps = (state: any) => {
const orderListState = state.orderList;
const orderListHomeState = state.home;
return {
...orderListState,
...orderListHomeState,
};
};
export default connect(
mapStateToProps,
{
addTabPage,
loadEnum,
}
)(OrderList);
将store传入react:
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
render(
,
document.getElementById('root')
)
异步action:在action中发起api请求
// 获取服务状态,现在一般这么使用,直接一个函数里面包括action,导出这个函数
export function requestServiceStatusList() {
return async (dispath: Dispatch) => {
const res: any = await serviceStatusList();
if (res && res.re) {
dispath({
type: DriverSpreadActionType.GET_SERVICESTATUS_LIST_DATA,
payload: {
serviceStatusList: res.re.statusList,
},
});
}
};
}
控制台log每一步的store并且可以使用异步action,thunk可以将action变成返回函数的函数
import { createStore, combineReducers, applyMiddleware } from 'redux'
let todoApp = combineReducers(reducers)
let store = createStore(
todoApp,
// applyMiddleware() 告诉 createStore() 如何处理中间件
applyMiddleware(logger, crashReporter)
)
之前项目中
import reduxThunk from 'redux-thunk';
const middlewares = [reduxThunk];
if (process.env.NODE_ENV === 'development') {
const { logger } = require('redux-logger'); // eslint-disable-line
middlewares.push(logger);
}
store = createStore(
reducer(),
initState,
composeEnhancers(applyMiddleware(...middlewares))
);
const routes = [
{
exact: true, // 是否是默认
path: '/',
component: () => ,
},
{
path: '/home',
component: Home,
},
{
path: '/login',
component: Login,
},
{
path: '/404',
component: Login,
},
{
path: '/*',
component: () => ,
},
];
// 顶层
return (
{routes.map((route: any, index: number) => (
))}
);