React复习

前言

本文主要会介绍React的主要学习过程,记录在学习的过程中遇到的知识点


一、React常见语法介绍

  • 在react中事件名字使用驼峰的命名方式

二、使用function定义组件(推荐)

1.解构赋值介绍

  • 解构赋值,把children单独取出来,把剩下的放在rest变量中
  • 每一个组件都有一个children 表示标签内部的子节点
const Counter = ({ children, ...rest }) => {
  return (
    <>
      <button>按钮</button>
      {children}
    </>
  );
};

2.父组件向子组件中传参

在组件定义的时候,可以接收一个props值,表示父组件传递过来的数据

const Amount = (props) => {
  console.log(props)
  return (
    <>
      <h1>{props.msg}</h1>
      <div>添加子组件</div>
    </>
  )
}
function App() {
  return (
    <div className="main">
      <Amount msg="我是你爸爸" />
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('app'))

3.子组件向父组件传参

子组件使用方法调用,直接调用父组件传递的方法

const Son = ({ handle }) => {
  const btnClick = () => {
    handle('小白')
  }
  return <button onClick={btnClick}>按钮</button>
}
function App() {
  const handle = (name) => {
    console.log(name)
  }
  return (
    <div className="main">
      <Son handle={handle} />
    </div>
  )
}

4.非相关组件之间的传参

以后在写,设计到以后使用的方法


三、在function组件中

React在v16.8 的版本中推出了 React Hooks 新特性,Hook是一套工具函数的集合,它增强了函数组件的功能,hook不等于函数组件,所有的hook函数都是以use开头

1.useState

在react中定义局部组件的状态,当组件的状态或者属性信息发生改变时,组件会重新渲染

  • useState接收一个参数作为初始化
  • 返回一个数组作为结果
  • 数组的第一项表示可以使用的变量名
  • 数组的第二项表示改变数据的方法,记得改变数据只能调用这个方法,否则组件不会重新渲染

案例:使用useState完成代办事项案例

const ToDo = () => {
  const { useState } = React
  const [list, setList] = useState([])
  return (
    <>
      <input
        type="text"
        placeholder="请输入添加的任务"
        onKeyUp={addHandle}
      />
      <ul>
        {list.map((item, index) => (
          <li key={item.id}>
            {item.todoName}
            <button onClick={() => delHandle(index)}>删除</button>
            {item.isEnd ? null : (
              <button onClick={() => finishHandle(index)}>完成</button>
            )}
          </li>
        ))}
      </ul>
    </>
  )
  // 添加函数--添加代办任务
  function addHandle(evt) {
    let e = evt || window.event
    if (e.keyCode == 13 && e.target.value) {
      setList([
        ...list,
        {
          id: Math.random() * 9999, //任务id
          todoName: e.target.value, //任务名字
          isEnd: false, //是否完成
        },
      ])
      e.target.value = ''
    }
  }
  // 删除函数--根据索引删除
  function delHandle(index) {
    setList(list.filter((item, current) => current != index))
  }
  // 修改目前任务的状态
  function finishHandle(index) {
    setList(
      list.map((item, current) => {
        if (current == index) {
          return {
            ...item,
            isEnd: true,
          }
        } else {
          return { ...item }
        }
      })
    )
  }
}
function App() {
  return (
    <div className="main">
      <ToDo />
    </div>
  )
}
ReactDOM.render(<App />, document.querySelector('#app'))

2.useEffect

useEffect叫副作用,接收两个参数,参数一是回调函数,参数二是一个依赖数组

  • 当数组中的数据改变的时候回调函数会重新执行
  • 如果参数二为空数组,那么只有在初始化的时候执行一次
  • 如果不传递参数二,那么每一次组件更新都会执行回调函数
  • useEffect的参数一如果返回一个function,那么这个function在组件销毁的时候执行
  • useEffect的作用是模拟组件生命周期函数
const [count, setCount] = useState(0)
useEffect(() => {
  console.log('不传第二个参数')
})
useEffect(() => {
  console.log('第二个参数为空数组')
}, [])
useEffect(() => {
  console.log('参数为count')
}, [count])
const { useState, useEffect } = React
const Destory = () => {
  useEffect(() => {
    console.log('该组件创建了')
    return () => {
      console.log('该组件销毁了')
    }
  }, [])
  return <h1>这个组件会被销毁</h1>
}
function App() {
  const [isshow, setShow] = useState(true)
  return (
    <div className="main">
      <button
        onClick={() => {
          setShow(!isshow)
        }}
      >
        隐藏
      </button>
      {isshow ? <Destory /> : <h1>被销毁后显示</h1>}
    </div>
  )
}

3.memo

  • memo的作用是对组件做缓存,每当组件的属性数据没有更新的时候组件不会重新渲染
  • 建议所有的自定义组件如果可以都使用memo做一次修饰
const { useState, memo } = React;
const Item = (props) => {
  console.log("组件渲染了");
  return <li>{props.content}</li>;
};
const MItem = memo(Item);
function App() {
  const [list, setList] = useState([]);
  const keyUpHandle = (e) => {
    if (e.keyCode === 13 && e.target.value) {
      setList([
        ...list,
        {
          id: Date.now(),
          content: e.target.value,
        },
      ]);
    }
  };
  return (
    <div className="main">
      <input type="text" placeholder="请输入内容" onKeyUp={keyUpHandle} />
      <ul>
        {list.map((item) => (
          <MItem {...item} key={item.id} />
        ))}
      </ul>
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("app"));

4.useCallback

  • 缓存一个function
  • 参数一 表示function
  • 参数二 表示依赖项,当依赖项改变的时候function会重新赋值
const { useState, useEffect, memo, useCallback } = React
const Count = ({ step, countHandle }) => {
  console.log('组件更新了')
  // 设置count的初始值为1
  const [count, setCount] = useState(1)
  useEffect(() => {
    countHandle(count)
  }, [count])
  return (
    <button onClick={() => setCount(count + step)}>
      count:{count}--step:{step}
    </button>
  )
}
// 对该组件进行缓存,若数据不发生改变则不重新渲染该组件
const MCount = memo(Count)
function App() {
  const [step, setStep] = useState(1)
  // 这是测试用例
  const countHandle = useCallback((a) => {
    console.log('当前的数值为' + a)
  }, [])
  const [num, setNum] = useState(1)
  return (
    <div className="main">
      <input
        type="range"
        step="1"
        defaultValue="2"
        min="1"
        max="10"
        style={{ width: '80%' }}
        onChange={(e) => {
          // console.log(e.target.value);
          setStep(e.target.value * 1)
        }}
      />
      <br />
      <MCount step={step} countHandle={countHandle} />
      <br />
      <button onClick={() => setNum(num + 1)}>测试按钮:{num}</button>
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('app'))

5.useMemo

只有当依赖的值发生变化才改变当前的值

 const { useState, useMemo } = React
 function App() {
   const [num1, setNum1] = useState(2)
   const [num2, setNum2] = useState(1)
   // 只有当依赖的值发生变化,才改变当前的值
   const s = useMemo(() => num1, [num2])
   return (
     <div className="main">
       <button onClick={() => setNum1(num1 + 1)}>num1:{num1}</button>
       <button onClick={() => setNum2(num2 + 1)}>num2:{num2}</button>
       <h1>{s}</h1>
     </div>
   )
 }
 ReactDOM.render(<App />, document.getElementById('app'))

6.useContext

使用上下文,主要用于非相关组件的传参

const { useState, useContext, createContext } = React
const context = createContext()
const Product = () => {
  const c = useContext(context)
  return <li>{c.name}</li>
}
const Products = () => {
  return (
    <ul>
      <Product />
    </ul>
  )
}
function App() {
  return (
    <div className="main">
      <Products />
    </div>
  )
}
const AppProvider = ({ children }) => {
  const [count, setCount] = useState(1)
  return (
    <context.Provider value={{ name: 'chen' }}>
      {children}
    </context.Provider>
  )
}
ReactDOM.render(
  // <context.Provider value={{ name: 'chen' }}>
  //   <App />
  // </context.Provider>,
  <AppProvider>
    <App />
  </AppProvider>,
  document.getElementById('app')
)

7.useReducer

  • useReducer 接收两个参数,参数一时一个function,参数二是初始值
  • 我们如果要改变数据的时候使用dispatch派发一个action改变
export default function Counter () {
  const [state,dispatch] = useReducer((state,action) => {
    switch (action.type) {
      case 'Add':
        return { ...state, count: state.count + action.payload.step }
      default:
        return {...state}
    }
  }, { count: 1, list: [] })
//对reducer进行封装
export default function dataReducers(state, action) {
  switch (action.type) {
    // 实现加法
    case 'Add':
      return { ...state, count: state.count + action.payload.step }
    // 实现减法
    case 'Sub':
      return { ...state, count: state.count - action.payload.step }
    default:
      return { ...state }
  }
}

import dataReducers from '../reducers/reducer'
const [state, dispatch] = useReducer(dataReducers, { count: 1, list: [] })
//触发
<button
  onClick={() => {
    dispatch({
      type: 'Add',
      payload: {
        step: 2,
      },
    })
  }}
>
  按钮{state.count}
</button>

四、Class定义组件

1.class定义组件

const { Component } = React
class App extends Component {
  constructor(props) {
    // 初始化的时候执行,可以获取属性和设置状态
    super(props)
    // 这句话的作用是为组件定义一个局部状态
    this.state = {
      count: 1,
    }
  }
  addCount() {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    return (
      <div className="main">
        <button onClick={() => this.addCount()}>
          点击按钮{this.state.count}
        </button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.querySelector('#app'))

2.this指向问题

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 1,
    }
    this.addCount2 = this.addCount2.bind(this)
  }
  // 使用箭头函数
  // 使用初始化的时候改变this
  // 使用bind
  addCount1() {
    this.setState({ count: this.state.count + 1 })
  }
  addCount2() {
    // console.log(this)
    this.setState({ count: this.state.count + 1 })
  }
  addCount3() {
    this.setState({ count: this.state.count + 1 })
  }
  render() {
    return (
      <div className="main">
        <button onClick={() => this.addCount1()}>
          按钮A{this.state.count}
        </button>
        <button onClick={this.addCount2}>按钮B{this.state.count}</button>
        <button onClick={this.addCount3.bind(this)}>
          按钮C{this.state.count}
        </button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.querySelector('#app'))

3.父子组件传参

const { PureComponent } = React
class Count extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
    }
  }
  render() {
    return (
      <button
        onClick={() => {
          this.setState({ count: this.state.count + 1 }, function () {
            this.props.changeNum(this.state.count)
          })
        }}
      >
        子组件{this.state.count}
      </button>
    )
  }
}
class App extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      num: 0,
    }
    this.changeNum = this.changeNum.bind(this)
  }
  changeNum(v) {
    this.setState({ num: v })
  }
  render() {
    return (
      <div className="main">
        <Count changeNum={this.changeNum} />
        <h1>{this.state.num}</h1>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.querySelector('#app'))

4.非相关组件传参

// 创建上下文
const context = createContext()
const AppProvider = ({ children }) => {
  const [test, setTest] = useState(20)
  return (
    <context.Provider value={{ name: 'chen', test, setTest }}>
      {children}
    </context.Provider>
  )
}
App.contextType = context

5.React生命周期

16.3之前React生命周期函数

componentDidMount() {
  console.log("组件挂载完成");
}
componentWillMount() {
  console.log("组件将要挂载");
}
shouldComponentUpdate(nextProps, nextState) {
  console.group("组件是否需要更新");
  console.log(nextProps, nextState);
  console.log(this.props, this.state);
  // 判断是否需要更新,这个生命周期钩子函数主要用来做性能优化
  //  如果改变的数据不需要在页面中进行展示,我们可以不更新组件 返回false
  // console.log("组件是否需要更新");
  console.groupEnd();
  return this.state.count != nextState.count; //nextState.count % 2 == 0;
}
componentWillUpdate() {
  console.log("组件将要更新");
}
componentDidUpdate() {
  console.log("组件更新完成");
}
componentWillReceiveProps(nextProps) {
  console.group("组件接收到新的属性");
  console.log(nextProps);
  console.groupEnd();
}
componentWillUnmount() {
  console.log("组件将要销毁");
}

五、路由

所有的路由组件必须包含在<Router>包含的节点中使用

1.路由配置

  • 在index.js中 import { HashRouter as Router } from “react-router-dom”;
<Router>
  <App/>
</Router>

2.使用路由

  • import {Route,Switch,Link} from ‘react-router-dom’
<Switch>
        {/* exact 表示完全匹配 */}
        {/* 这种写法是推荐的 */}
        <Route path="/" exact>
          <Home />
        </Route>
        {/* component属性表示访问这个path的时候展示的组件 */}
        {/* 当我们使用component方式定义路由的组件的时候,会在组件中自动的把当前路由数据集成在组件的属性中 */}
        <Route path="/list" component={List} />
        {/* render是一个function,返回一个组件 */}
        {/* params传参需要加占位符,占位符后面的?表示是一个可选参数 */}
        <Route path="/detail/:p?" render={() => <Detail />} />
        <ProtectedRoute path="/user">
          <User />
        </ProtectedRoute>
        <Route path="/login">
          <Login />
        </Route>
      </Switch>

3.配置私有路由

  • 私有路由,做登录判断
  • 使用结构赋值获取children和剩余的属性
import { Route, Redirect } from "react-router-dom";
import { isLogined } from "../utils/tools";
function ProtectedRoute({ children, ...rest }) {
  return (
    <Route
      {...rest}
      // render的作用就是返回一个当前对应路径展示的组件信息
      // Redirect表示重定向
      render={() => (isLogined() ? children : <Redirect to="/login" />)}
    />
  );
}

4.路由传参

  • // 路由传参的时候 可以直接把to定义为一个对象
  • // pathname表示路径
  • // search表示url中传递的参数
  • // state也可以传参,但是页面刷新之后就没了
<Link
  to={{
    pathname: '/detail',
    search: 'id=2&age=15',
    state: { skills: '水枪', author: '小智' },
  }}
>

5.路由中的hooks

  • withRouter 是一个高阶函数
    作用是对组件做一个封装,把路由数据填充在组件的props属性中,当我们使用component定义路由组件时,会自动将路由数据集成 在组件的属性中
  • useLocation 获取当前路由中的location数据
  • useParams 获取当前路由中的params数据
  • useHistory 获取当前的history数据,可以实现编程式跳转
function Detail(props) {
  const { search, state } = useLocation()
  console.log(state)
  const params = new URLSearchParams(search)
  console.log(params.get('id'))
  // console.log(useParams())
  // console.log(useHistory())
  return <div>这里是详情页</div>
}
export default withRouter(Detail)

六、redux

1.redux的基础使用

  • 在redux中如果要改变数据只能dispatch派发一个action,在reducer中进行改变
  • 在react中可以使用插件react-redux把redux中的数据传递到react组件中
// import { createStore } from 'redux'
import { Provider } from 'react-redux'
import store from './store'
// function AppReducer(state = { id: 1, count: 1 }, action) {
//   switch (action.type) {
//     case 'PLUS':
//       return { ...state, count: state.count + 1 }
//     default:
//       return { ...state }
//   }
// }
// const store = createStore(AppReducer)
//  在redux中如果要改变数据只能dispatch派发一个action,在reducer中进行改变
// store.disspatch({ type: 'PLUS' })
// 在react中可以使用插件react-redux把redux中的数据传递到react组件中
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

2.redux封装

import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import appReducer from './reducer/app'
import todoReducer from './reducer/todo'
import productReducer from './reducer/product'
// combineReducers合成多个reducer为一个
const store = createStore(
  combineReducers({
    appReducer,
    todoReducer,
    productReducer,
  }),
  compose(
    applyMiddleware(...[thunk]),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
)
export default store

reducer

const todoReducer = (
  state = {
    list: [
      { id: 1, name: '喜羊羊' },
      { id: 2, name: '懒羊羊' },
    ],
  },
  action
) => {
  switch (action.type) {
    case 'addTask':
      return { ...state, list: [...state.list, action.payload] }
    case 'delTask':
      return {
        ...state,
        list: state.list.filter((item) => item.id !== action.payload),
      }
    default:
      return state
  }
}
export default todoReducer

const productReducer = (state = { product: [] }, action) => {
  switch (action.type) {
    case 'getProduct':
      return { ...state, product: [...state.product, ...action.payload] }
    default:
      return state
  }
}
export default productReducer

action

import { loadProductApi } from '../../services/productApi'
export const getProductHandle = () => (dispatch) => {
  loadProductApi().then((res) => {
    dispatch({
      type: 'getProduct',
      payload: res.products,
    })
  })
}

3.使用插件完成

七、使用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值