react起步

470 阅读2分钟
  • 安装官⽅方脚⼿手架:npx create-react-app my-react-app
  • 进入:cd my-react-app
  • 启动项目npm start
  • 暴露配置项npm run eject

如果出现错误类似/babel-preset-reactapp/node_modules/@babel/runtime/helpers/slicedToArray' at webpackMissingModule ‘,就npm add @babel/runtime

setState

  • setState是批量量执行的,因此对同一个状态执行多次只起一次作用,多个状态更新可以放在同一个setState中进⾏

  • setState通常是异步的,因此如果要获取到最新状态值有以下三种⽅方式:

//传递函数给setState⽅方法
this.setState((nextState, props) => ({counter: state.counter + 1}));// 
//使用定时器
setTimeout(() => {
    this.changeValue();
}, 0);
//原⽣生事件中修改状态
componentDidMount(){
    document.body.addEventListener('click',this.changeValue, false)
}
changeValue = () => {
    this.setState({counter:this.state.counter+1})
    console.log(this.state.counter)
}

为什什么 setState只有在React合成事件和⽣生命周期数中是异步的,在原⽣生事件和setTimeout、setInterval、addEventListener中都是同步的?

原⽣生事件绑定不会通过合成事件的⽅式处理,自然也不会进入更新事务的处理流程。setTimeout也一样,在setTimeout回调执行时已经完成了原更新组件流程,也不会再进入异步更新流程,其结果自然就是同步的了。

组件通信

  • 子传父
// index.js
ReactDOM.render(<App title="首页" />,
document.querySelector('#root'));
// App.js
<h2>{this.props.title}</h2>
  • 父传子(状态提升)
// StateMgt
<Clock change={this.onChange}/>

// Clock
this.timerID = setInterval(() => {
    this.setState({
        date: new Date()
    }, ()=>{
        // 每次状态更新就通知父组件
        this.props.change(this.state.date);
    });
}, 1000);
  • 跨层级组件之间通信 context
//AppContext.js
import React, { Component } from 'react'
export const Context = React.createContext()
export const Provider = Context.Provider
export const Consumer = Context.Consumer

//App.js
import { Provider } from './AppContext'
const store = {
    home:{},
    user:{}
}
function App() {
    return (
        <div className="app">
            <Provider value={store}>
                <Home />
            </Provider>
        </div>
    );
}

//Home.js
import { Consumer } from '../AppContext';
function Home(){
    return (<Consumer>{ctx => <HomeCmp {...ctx} />}</Consumer>)
}

//HomeCmp.js
function HomeCmp(props) {
    const { home, user } = props
    return (...)
}

高阶组件

function Child(props) {
    return <div>Child</div>
}
const foo = Cmp => props => {
    return <Cmp {...props} />
}

//使用
const Foo = foo(Child)
<Foo />

//链式调用
const Food = foo(foo(Child))
<Food />
  • consumer结合高阶组件
const handleConsumer = Cmp => props => {
    return <Consumer>{ctx => <Cmp {...ctx} {...props}></Cmp>}</Consumer>
}

//使用
const HandleConsumer = handleConsumer(UserCmp)
<HandleConsumer />

装饰器

1.npm run eject

2.配置package.json

"babel": {
    "presets": [ "react-app" ],
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {"legacy": true}
        ]
    ]
}

3.安装装饰器器插件 npm install @babel/plugin-proposal-decorators -- save-dev

4.使用在class组件上

const foo = Cmp => props => {
    return (
        <div className="border">
            <Cmp />
        </div>
    );
};

@foo
@foo
class xxx ...

useContext

import React, { useContext } from "react";
import { Context } from "../AppContext";
export default function UseContextPage() {
    const ctx = useContext(Context);
    const { name } = ctx.user;
    return (
        <div>
            <h1>UseContextPage</h1>
            <p>{name}</p>
        </div>
    );
}

useReducer

function fruitReducer(state = [], action) {
    switch (action.type) {
        case "replace":
        case "init":
            return [...action.payload];
        case "add":
            return [...state, action.payload];
        default:
            return state;
    }
}
export default function UseReducerPage() {
    const [fruits, dispatch] = useReducer(fruitReducer,[]);
    useEffect(() => {
        setTimeout(() => {
            dispatch({ type: "init", payload: ["apple","banana"] });
        }, 1000);
        return () => {};
    }, []);
    return (
        <div>
            <h1>UseReducerPage</h1>
            <FruitAdd fruits={fruits} addFruit={name => dispatch({ type: "add", payload: name })}/>
            <FruitList fruits={fruits} setFruits={newList => dispatch({ type: "init",payload: newList })}/>
        </div>
    );
}