从0到1开始教你学习react

212 阅读9分钟

1、创建项目

npx create-reacte-app demo

2、运行

npm run start

3、删除src下面所有文件,并新建index.js文件

import ReactDom from 'react-dom'
import App from './App.jsx'
ReactDom.render(
    <App/>,//组件名
    document.getElementById('root')
)

4、编写的第一个类组件

import React from "react";
let msg = '111'
//类组件
class App extends React.Component{
    render() {
        return(
            <div>
                <h1>{msg}</h1>
            </div>
        )
    }
}

export default App

5、快速创建react组件

es7+
rcc = react class component //类组件
rfc = react function component //函数式组件

image.png

image.png

image.png

image.png

6、示例

image.png

7、注意

1、组件文件可以是js或者jsx,不影响,但是建议组件用jsx,方法用js
2、js中出现()就代表想写html
3、js中出现{}代表想要写js
4、组件名必须要大写
5、其实export default可以写在class前面

8、动态绑定class类和写入内联样式

import React from "react";
let msg = '111'
let style = {color:'red'}
import "./app.css"
import appModule from './appModule.css'
//类组件
class App1 extends React.Component{
    render() {
        return(
            <div>
                <label htmlFor="username">用户名</label> //htmlFor代表for,可以理解成label与哪个表单组件绑定
                <input type="text" id="username"/>
                <h1 className="box">{msg}</h1> //绑定class类
                <h1 style={style}>{msg}</h1> //通过变量写入样式
                <h1 style={{color:'red'}}>{msg}</h1> //{{}}代表对象中的对象,里面写入样式
            </div>
        )
    }
}

export default App1

// 1、className=“box” 需要在外层新建一个css文件,并在组件文件中引入
// 2、类的唯一 className={ appModule['box']}

9、for循环(类似v-for)

let arr = ['刘备','关羽']
//类组件
class App1 extends React.Component{
    render() {
        return(
            <div>
                {arr.map((item,index)=><li key={index}>{item}</li>)}
            </div>
        )
    }
}
export default App1

//react中只能使用map,因为只有map才有返回值
//不能使用for foreach,因为这里返回的undefined

10、点击事件和修改值

import React from "react";
let msg = '111'
let style = {color:'red'}
let arr = ['刘备','关羽']
//类组件
class App1 extends React.Component{
    state = {
        num: 10
    } // 类似vue中的data
    render() {
        return(
            <div>
                <label htmlFor="username">用户名</label>
                <input type="text" id="username"/>
                <h1 className="box">{msg}</h1>
                <h1 style={style}>{msg}</h1>
                <h1 style={{color:'red'}}>{msg}</h1>
                {arr.map((item,index)=><li key={index}>{item}</li>)}
                <p>{this.state.num}</p>
                <button onClick={()=>this.setState({num:this.state.num + 1})}>点击</button> //通过this.setState来修改state中的值 this.state.num获取值
            </div>
        )
    }
}

export default App1

// state 类似vue中的data
// 通过this.setState来修改state中的值 
// this.state.num获取值
//onClick={()=>需要处理的事件}

11、state(类似vue中的data)完整写法

class App1 extends React.Component{
    //完整版
    constructor(props) {
        super(props);
        this.state = {
            num:1
        }
    }
    //简写
    state = {
        num: 10
    } // 类似vue中的data
    render() {
        return(
            <div>
                <p>{this.state.num}</p>
                <button onClick={()=>this.setState({num:this.state.num + 1})}>点击</button> //通过this.setState来修改state中的值 this.state.num获取值
            </div>
        )
    }
}

12、onclick和setState的三种写法

import React from "react";
//类组件
class App1 extends React.Component{
    //方法4
    constructor(props) {
        super(props);
        this.state = {
            num:1
        }
       this.add = this.add.bind(this)
    }
    render() {
        return(
            <div>
                <p>{this.state.num}</p>
                <button onClick={()=>this.setState({num:this.state.num + 1})}>方法1</button>
                <button onClick={this.add.bind(this)}>方法2</button>
                <button onClick={()=>this.add()}>方法3</button>
                <button onClick={this.add}>方法四</button>
            </div>
        )
    }
    add(){
        this.setState({num:this.state.num + 1})
    }
}

export default App1

// 1、方法1箭头函数写法,适用于代码量少的强框
// 2、方法2 在外层写一个方法,并在里面调用,同时要通过.bind来更改this的指向,否则报错,this为undefined 不推荐
// 3、箭头函数直接调用 推荐
// 4、在constructor下面调用该方法并改变this指向
// 5、注意onClick={this.add.bind()}不要加入(),否则就是直接进入页面直接调用

13、函数式组件

//rsf
import React from 'react';

function App1(props) {
    return (
        <div></div>
    );
}

export default App1;

/*
 1、函数式组件没有生命周期
 2、函数式组件没有this
 3、函数式组件没有state
 */

14、hooks

1、钩子 理解的意思大概就是类似vue中的生命周期
2、分两种:react官方提供的hook、开发人员自定义的hook

14-1 官方提供的hook,通过hook改变值(useState)

image.png

14-2 官方提供的hook,通过hook请求数据(useEffect),类似vue中的mounted、updated、beforeDestro

useEffect就是react中mounted、updated、beforeDestroy的结合体

import {useState,useEffect} from "react";

function App2(props) {
    //hook只能用在函数最顶层
    let [mag,setMsg] = useState(1)
    //模拟nounted,一般在这个位置写ajax
    // useEffect(()=>{
    //     console.log('检测到mag的数据更新了')
    //     btn()
    // })
    //检测数据更新,检测哪个数据更新,把这个变量填写到数组中
    //当要检测页面中所有的变量更新,你有两个选择,一是把所有变量填写到数组中去,二是直接不写数组
    //如果你不想检测页面中数组的变化,直接给一个空数组
    // useEffect(()=>{
    //     console.log('检测到mag的数据更新了')
    // },[mag])
    // let btn = ()=>{
    //     console.log(123)
    // }
    //模拟beforeDestroy,一般在这个阶段处理脏数据或者垃圾回收
    useEffect(()=>{
        return ()=>{
            console.log('销毁阶段')
        }
    })
    return (
        <div>
            <h1 onClick={()=>setMsg(1)}>{mag}</h1>
            {/*实现累加*/}
            <h1 onClick={()=>setMsg(mag++)}>{mag}</h1>
        </div>
    );
}

export default App2;

/*
 1、函数式组件没有生命周期
 2、函数式组件没有this
 3、函数式组件没有state
 4、[vue生命周期]mounted(数据请求) updated(检测数据更新) beforeDestroy(立即回收)
 5、useEffect就是react中mounted、updated、beforeDestroy的结合体
 6、useState用于修改值
 */

15、父传子(props)

import React from 'react';

//子组件
function Child(props) {
    return (
        <div>
            //子组件接收父组件的值 props
            <h1>{props.num}</h1>
        </div>
    );
}

//父传子 props
function App3(props) {
    return (
        <div>
            <Child num={1}/>
        </div>
    );
}

export default App3;

16、子传父

import React,{useState}from 'react';

//子组件
function Child(props) {
    return (
        <div>
            <h1>{props.num}</h1>
            <button onClick={()=>props.changeNumFn()}>子传父</button>
        </div>
    );
}

//父传子 props
function App3(props) {
    let [num,serNum] = useState(1)
    return (
        <div>
            <Child num={num} changeNumFn={()=>serNum(num+1)}/>
        </div>
    );
}

export default App3;

17、跨组件传值(createContext)适用于父向孙子传值

17-1完整版写法繁琐,不推荐)

import React,{useState,createContext}from 'react';
const NumContext =  createContext();
//子组件
function Child() {
    return (
       <NumContext.Consumer>
           {
               ({num,setNum})=>(
                   <div>
                       <h2>{num}</h2>
                       <button onClick={()=>setNum(num+1)}>点击改变</button>
                   </div>
               )
           }
       </NumContext.Consumer>
    );
}

//父组件
const Father = ()=><Child/>

//顶级组件
function App3(props) {
    let [num,setNum] = useState(2)
    return (
      <NumContext.Provider value={{num,setNum}}>
          <Father></Father>
      </NumContext.Provider>
    );
}

export default App3;

17-2 简写,通过useContext来获取num,setNum的值(推荐)

import React,{useState,createContext,useContext}from 'react';
const NumContext =  createContext();
//子组件
function Child() {
    let {num,setNum} = useContext(NumContext)
    return (
        <div>
            <h2>{num}</h2>
            <button onClick={()=>setNum(num+1)}>点击改变</button>
        </div>
    );
}

//父组件
const Father = ()=><Child/>

//顶级组件
function App3(props) {
    let [num,setNum] = useState(2)
    return (
      <NumContext.Provider value={{num,setNum}}>
          <Father></Father>
      </NumContext.Provider>
    );
}

export default App3;

18、受控组件(类似vue中的v-model)

import React,{useState}from 'react';

function App3(props) {
    let [value,setValue] = useState(2)
    return (
       <>
        <input value={value} onChange={(e)=>setValue(e.target.value)}/>
           <button onClick={()=>console.log(value)}></button>
       </>
    );
}

export default App3;

/*
  受控组件和不受控组件只存在表单于元素中,类似vue中的v-model
  所谓受控组件就是表单元素的value需要通过state(类组件)(或者useState(函数式组件))来定义
 */

19、不受控组件(useRef)类似vue中的ref

import React,{useState,useRef}from 'react';

function App3(props) {
    let dom = useRef(null)
    return (
       <>
           <input type="text" ref={dom}/>
           <button onClick={()=>console.log(dom.current.value)}>不受控组件              </button>
       </>
    );
}

export default App3;

/*
  不受控组件意味着表单元素的value无法通过state获取,只能通过ref或者useRef来获取
 */

20、memo(当父组件更新本身数据,但是与子组件毫无干系,可还是强制刷新了子组件数据,用memo来避免)只适用于静态值,不推荐

import React,{useState,memo}from 'react';

const Child = memo(()=>{
        console.log('我被负组件更新了')
        return <div></div>
})
//父组件
function App5(props) {
    const [num,setNum] = useState(123)
    return (
        <div>
            <h1>{num}</h1>
            <button onClick={()=>setNum(123)}></button>
            <Child></Child>
        </div>
    );
}

export default App5;

21、解决方案(useCallback)(useCallback必须配合memo使用,且只用于点击事件)

import React,{useState,memo,useCallback}from 'react';

const Child = memo((props)=>{
        console.log('我被负组件更新了')
        return <div onClick={()=>props.dSth()}></div>
})
//父组件
function App5(props) {
    const [num,setNum] = useState(123)
    /*
     setNum(newValue)使用新值覆盖旧值
     setNum((num)=>num+1)不断使用新值覆盖旧值
     */
    //不推荐 只会执行一次
    // const dSth = useCallback(()=>setNum(num + 1),[])
    //不断被执行
    const dSth = useCallback(()=>setNum((num)=>num + 1),[])
    return (
        <div>
            <h1>{num}</h1>
            <button onClick={()=>setNum(123)}></button>
            <Child dSth={dSth}></Child>
        </div>
    );
}

export default App5;

22、可替代useCallback方案(useMemo),简写更实用(类似vue中的计算属性)

import React,{useState,memo,useMemo}from 'react';

const Child = memo((props)=>{
        console.log('我被负组件更新了')
        return <div onClick={()=>props.dSth()}></div>
})
//父组件
function App5(props) {
    const [num,setNum] = useState(123)
    /*
     setNum(newValue)使用新值覆盖旧值
     setNum((num)=>num+1)不断使用新值覆盖旧值
     */
    //不推荐 只会执行一次
    // const dSth = useCallback(()=>setNum(num + 1),[])
    //不断被执行
    //const dSth = useCallback(()=>setNum((num)=>num + 1),[])
    //可替代useCallback方案,简写更实用
    const dSth = useMemo(()=>{
        return ()=>setNum(num + 1)
    },[])
    return (
        <div>
            <h1>{num}</h1>
            <button onClick={()=>setNum(123)}></button>
            <Child dSth={dSth}></Child>
        </div>
    );
}

export default App5;

23、react-redux(类似vue中的vuex)

1、 安装

npm i redux react-redux

2、 创建文件夹store,并在下方创建index和reducer文件

npm i redux react-redux

image.png

3、编写文件reducer.js文件

//创建一个初始的状态
//defaultState类似vuex中的state
const defaultState = {
    num:1
}

//导出一个函数
export default (state = defaultState)=>{
    return state
}

4、编写index.js

//入口文件
//引入redecer
import reducer from "./reducer";
//创建仓库
import {createStore} from 'redux'

const store = createStore(reducer)

//导出仓库
export default store

5、使用和获取redux的值

1、在index.js文件中引入,类似main.js
/*
import {Provider} from 'react-redux'
import store from './store'
  <Provider store={store}>
        <App6/>
  </Provider>,
*/

import ReactDom from 'react-dom'
import App6 from './App6.jsx'
import {Provider} from 'react-redux'
import store from './store'
ReactDom.render(
    <Provider store={store}>
        <App6/>
    </Provider>,
    document.getElementById('root')
)

2、再到对应文件组件中引入---获取值

import React from 'react';
import {connect} from "react-redux";

function App6(props) {
    return (
        <div>{props.num}</div>
    );
}

//状态映射:将reducer中state映射成props,让开发者可以在组件中使用props.uum去调用state中的num
const mapStateToProps = (state)=>{
    return {
        num:state.num
    }
}

//export default connect(state映射,dispathc映射)(组件名称);
export default connect(mapStateToProps)(App6);

3、事件派发,类似vue中的vuex调用事件并修改值
//组件内
import React from 'react';
import {connect} from "react-redux";

function App6(props) {
    return (
        <div>
            {props.num}
            <button onClick={()=>props.leijia()}>累计</button>
        </div>
    );
}

//状态映射:将reducer中state映射成props,让开发者可以在组件中使用props.uum去调用state中的num
const mapStateToProps = (state)=>{
    return {
        num:state.num
    }
}

//事件派发,类似vue中的vuex调用事件并修改值
const mapDispatchToProps = (dispatch) =>{
    return{
        leijia(){
            const action = {type:"addNum"}
            dispatch(action)
        }
    }
}

//export default connect(state映射,dispathc映射)(组件名称);
export default connect(mapStateToProps,mapDispatchToProps)(App6);

//模块内
//创建一个初始的状态
//defaultState类似vuex中的state
const defaultState = {
    num:1
}

//导出一个函数
export default (state = defaultState,action)=>{
    let newState = JSON.parse(JSON.stringify(state))
    if(action.type === 'addNum'){
        newState.num++
    }
    return newState
}

24、路由

1、安装

npm install react-router-dom@版本

2、创建router文件夹,并创建index.jsx文件

/*
App>Home>
react-router-dom中有两种模式:BrowserRouter(History模式) HashRouter(hash模式)
*/
import App from '../App'
import Home from '../pages/home'
import {BrowserRouter,Routes,Route} from "react-router-dom";
//定义一个路由
const BaseRouter = ()=>(
    <BrowserRouter>
        <Routes>
            <Route  path="/" element={<App></App>}>
                <Route path="/home" element={<Home></Home>}></Route>
            </Route>
        </Routes>
    </BrowserRouter>
)

export default BaseRouter

3、入口文件引入

import ReactDom from 'react-dom'
import Router from './router/index.jsx'
import {Provider} from 'react-redux'
import store from './store'
ReactDom.render(
    <Provider store={store}>
        <Router/>
    </Provider>,
    document.getElementById('root')
)

4、主视口文件引入(Outlet)类似vue中的router-view

import React from "react";
import {Outlet} from "react-router-dom";
//类组件
class App extends React.Component{
    render() {
        return(
            <div>
                <Outlet></Outlet>
            </div>
        )
    }
}

export default App

image.png

5、跳转(Link(类似vue中的vue-router),useNavigate(类似vue中的this.$router))

//<Link to="/home">跳转到首页</Link>
import React from "react";
import {Outlet,Link,useNavigate} from "react-router-dom";
class App extends React.Component{
    render() {
        console.log(123)
        const navicgate = useNavigate()
        const useNavigateBtn = ()=>{
            navicgate('/home')
        }
        return(
            <div>
                <p>
                    <Link to="/home">跳转到首页</Link>
                    <button onClick={useNavigateBtn}>跳转到首页</button>
                </p>
                <Outlet></Outlet>
            </div>
        )
    }
}

export default App

6、获取整个路由列表或者路径(useLocation),类似vue中的this.$route.path

import React from "react";
import {Outlet,Link,useLocation} from "react-router-dom";


function App(props) {
    const location = useLocation()
    console.log(location)
    return (
        <div>
            <Link to="/home">跳转到首页</Link>
            <Outlet></Outlet>
        </div>
    );
}

export default App;

image.png

6、跳转携带参数

1、方案1 在路径后面携带,获取参数(useParams)

import React from 'react';
import {useParams} from "react-router-dom";
function Home(props) {
    let {id} = useParams()
    console.log(id)
    return (
        <div>123123</div>
    );
}

export default Home;

image.png

image.png

image.png

2、方案2问号的形式/home?id=123

import React from 'react';
import {useSearchParams} from "react-router-dom";
function Home(props) {
    const [searchParams] = useSearchParams()
    let id = searchParams.getAll('id')[0]
    return (
        <div>123123</div>
    );
}

export default Home;

3、事件跳转携带参数,可携带多个参数和对象

navicgate('/home',{
    state:{
        username:'张三'
    }
})

取出
let Location = useLocation()
//从路由列表中取出
console.log(Location.state.username)

4、404设置

{/*404*/}
{/*<Route path="*" element={页面路径}></Route>*/}

注意:在页面中引入图片路径只能用impotr 不能使用绝对路径