CSS
styled-components: yarn add styled-components
// props透传
// attrs的使用
// 传入state作为props属性
const StyledInput = styled.input.attrs({
type: 'password',
placeholder:'请输入',
bgColor:'#000'
})`
color: ${props => props.color};
background-color: ${props => props.bgColor};
`
// 继承
const StyledInput2 = styled(StyledInput)`
margin-top: 20px;
padding: 10px;
`
动画
npm install react-transition-group --save
<CSSTransition
nodeRef={this.nodeRef}
in={this.state.show}
timeout={500}
classNames="fade"
unmountOnExit
appear
>
<h1 ref={this.nodeRef}>CSS Transition</h1>
</CSSTransition>
.fade-enter, .fade-appear{
opacity: 0;
}
.fade-enter-active, .fade-appear-active{
opacity: 1;
transition: opacity 500ms ease-in-out;
}
.fade-enter-done, .fade-appear-done{
opacity: 1;
}
.fade-exit{
opacity: 1;
}
.fade-exit-active{
opacity: 0;
transition: opacity 500ms ease-in-out;
}
.fade-exit-done{
opacity: 0;
}
Redux
// index.js
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
store.subscribe(() => {
console.log(store.getState())
})
store.dispatch(addAction(1))
store.dispatch(subAction(10))
// action.js
export const addAction= (num) => ({
type: ADD_NUMBER,
num
})
export const subAction= (num) => ({
type: SUB_NUMBER,
num
})
// reducer.js
const defaultlState = {
count: 0,
}
function reducer(state = defaultlState, action) {
switch (action.type) {
case ADD_NUMBER:
return {
...state,
count: state.count + action.num,
}
case SUB_NUMBER:
return {
...state,
count: state.count - action.num,
}
default:
return state
}
}
// 加入connect
// index.js
import { createStore, applyMiddleware } from 'redux'
**import { thunk } from 'redux-thunk'**
import reducer from './reducer'
const storeEnhancer = applyMiddleware(thunk)
const store = createStore(reducer, storeEnhancer)
export default store
// reducer.js
import { createStore, applyMiddleware } from 'redux'
import { thunk } from 'redux-thunk'
import reducer from './reducer'
const storeEnhancer = applyMiddleware(thunk)
const store = createStore(reducer, storeEnhancer)
export default store
// actionCreator.js
import { ADD_NUMBER, SUB_NUMBER } from './constans'
export const addAction= (num) => ({
type: ADD_NUMBER,
num
})
export const subAction= (num) => ({
type: SUB_NUMBER,
num
})
**export const getBannerList = (dispatch) => (
console.log('getBannerList')
)**
// connect.js
import { PureComponent } from "react";
import store from '../store'
export function connect(mapStateToProps, mapDispatchToProps) {
return function (Component) {
return class ConnectedComponent extends PureComponent {
constructor(props) {
super(props)
this.state = {
storeState: mapStateToProps(store.getState()),
}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState({
storeState: mapStateToProps(store.getState()),
})
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
return (
<Component
{...this.props}
{...this.state.storeState}
{...mapDispatchToProps(store.dispatch)}
/>
);
}
};
};
}
// home.js
import { connect } from '../utils/connect'
import { addAction, subAction, getBannerList } from '../store/actionCreator'
const mapStateToProps = (state) => ({
count: state.count
})
const mapDispatchToProps = (dispatch) => ({
addAction: (num) => dispatch(addAction(num)),
subAction: (num) => dispatch(subAction(num)),
getBannerList: () => dispatch(getBannerList)
})
function Home2(props) {
return (
<>
<div>
<h1>HOME</h1>
<h2>当前计数:{props.count}</h2>
</div>
<button onClick={() => props.addAction(1)}>+1</button>
<button onClick={() => props.subAction(1)}>-1</button>
<button onClick={() => props.getBannerList()}>获取轮播图</button>
</>
)
}
export default connect(mapStateToProps, mapDispatchToProps)(Home2)
router
npm install react-router-dom
BrowserRouter: history模式
HashRouter: hash模式
<BrowserRouter>
<Link to="/a">首页</Link>
<Link to="/about">关于</Link>
<Routes>
<Route path="/a" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
function Routes() {
return useRoutes([
{
path: '/home',
element: <Home />
},
{
path: '/about',
element: <About />,
children: [
{
path: 'history',
element: <AboutHistory />
},
{
path: 'school',
element: <AboutSchool />
},
{
path: 'company',
element: <AboutCompany />
}
]
}
])}
Hooks
class组件比函数式组件的优势(没有hooks的时候)
1.class组件可以定义自己的state,用来保存组件自己内部的状态
函数式组件不可以,因为函数每次调用都会产生新的临时变量
2.class组件有自己的生命周期,比如在componentDidMounted中发送网络请求,
并且在生命周期函数只会执行一次
在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求
3.class组件可以在状态改变时只会重新执行rendr函数以及生命周期componentDidUpdate
函数式组件在重新渲染时,整个函数都会被执行
useState
本身是一个函数,来自react包
参数和返回值
1.参数:给创建出来的状态一个默认值
2.返回值:数组,元素1:当前state的状态值,元素2是更新状态的函数
只能在函数最外层调用Hook,不要在循环,条件判断或者子函数中使用
只能在react的函数组件中调用hook,不能在其他js函数中调用
useCallback
// memo 用于包裹函数组件,使其在 props 未发生变化时跳过重新渲染,从而提升性能。
// 父组件重新渲染,子组件也会重新渲染,但是如果子组件被 memo 包裹,且 props 未发生变化,那么子组件就不会重新渲染。
// HYBButton1的increment函数会重新创建,所以memo包裹的props发生变化,会重新渲染
// HYBButton2的increment函数不会重新创建,所以memo包裹的props没有发生变化,不会重新渲染
const HYBButton = memo(({title, increment}) => {
console.log('重新渲染', title)
return (
<button onClick={increment}>
+1
</button>
)
})
export default function Callback() {
const [count, setCount] = useState(0)
const [isShow, setIsShow] = useState(true)
const handleClick1 = () => {
setCount(count + 1)
console.log(111)
}
const handleClick2 = useCallback(() => {
setCount(count + 1)
console.log(222)
},[count])
return (
<div>
<p>当前计数: {count}</p>
<p>当前显示状态: {isShow ? '显示' : '隐藏'}</p>
<HYBButton title="增加1" increment={handleClick1} />
<HYBButton title="增加2" increment={handleClick2} />
<button onClick={() => setIsShow(!isShow)}>显示/隐藏</button>
</div>
)
}
useContext
App.js
export const UserContext = createContext()
<React.StrictMode>
<UserContext.Provider value={{name: '张三', age: 18}}>
<MemoDemo />
</UserContext.Provider>
</React.StrictMode>
index.js
import { UserContext } from '../index'
export default function User() {
const user = useContext(UserContext)
return (
<div>
<p>当前用户: {user.name}</p>
<p>当前用户年龄: {user.age}</p>
</div>
)
}
useMemo
import { useState,useMemo,memo } from 'react';
function calcNum(count){
let total = 0
for(let i = 0; i < count; i++) {
total += i
console.log('total',total)
}
return total
}
const HYUser = memo(function HYUser(props) {
const {name, age} = props
console.log('HYUser', name, age)
return(
<div>
<p>当前用户: {name}</p>
<p>当前年龄: {age}</p>
</div>
)
})
export default function MemoDemo() {
const [count, setCount] = useState(0)
const [isShow, setIsShow] = useState(true)
// let total = 0
// for(let i = 0; i < count; i++) {
// // 切换show每次都会执行for循环
// total += i
// }
const total = useMemo(() => calcNum(count), [count])
const info = useMemo(() => ({name: '张三', age: 18}), []) // 依赖项为空数组,只在组件挂载时执行一次
//const info = {name: '张三', age: 18} 每次重新渲染都创建一个新对象,会导致HYUser组件重新渲染
return(
<div>
<p>当前计数: {count}</p>
<p>当前显示状态: {isShow ? '显示' : '隐藏'}</p>
<p>当前total: {total}</p>
<HYUser info={info} />
<button onClick={() => setCount(count + 1)}>增加</button>
<button onClick={() => setIsShow(!isShow)}>切换显示状态</button>
</div>
)
}
- useMemo :缓存值 ,用于优化复杂计算
- useCallback :缓存函数 ,用于优化子组件渲染
- useCallback 是 useMemo 的特例 : useCallback(fn, deps) 等价于 useMemo(() => fn, deps)
useReducer
import { useReducer } from 'react'
function reducer(state, action) {
switch(action.type) {
case 'increment':
return {counter: state.counter + action.payload}
case 'decrement':
return {counter: state.counter - action.payload}
default:
return state
}
}
export default function User() {
const [state, dispatch] = useReducer(reducer, {counter: 0})
return(
<div>
<p>当前状态: {state.counter}</p>
<button onClick={() => dispatch({type: 'increment', payload: 1})}>增加</button>
<button onClick={() => dispatch({type: 'decrement', payload: 1})}>减少</button>
</div>
)
}
useLayoutEffect
useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新
useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新
如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect