声明: 此文档很不完善,只是简约版本。用于本人学习React的简要记录,用于加深记忆。建议有相关疑问时,移步官网获取更新详细信息

JMX语法

类似模板语言 + JavaScript + HTML语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const name = "hello,world";
const element = <h1>hello, {name}</h1>
const className = "divBase";
// HTML属性名称改为驼峰命名
const element = <div className={className}>Hello,{name}</div>
// 包含多元素
const element = (
  <div>
    {/* 内部注释 */}
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

注意点:

  • JMX有且只有一个根元素,如最没有,可以使用<></>节点代替
  • 所有标签必须形成闭合
  • 返回多行,用()包括起来

元素渲染

元素渲染进React

1
2
const element = <h1>Hello,world</h1>
ReactDOM.render(element,  document.getElementById('root'));
  • 元素是构成React应用的最小砖块
  • 元素是不可变对象,一旦被创建,就无法更改它的子元素以及属性

组件

组件可以将UI拆分成独立的单元,并任意组合与复用。编写组件最好的方法就是编写javascript函数

当不需要维护相关状态时,可以用函数组件,简单

  • 2022年之后社区推荐方法,编写函数
1
2
3
function Hello(props) {
    return <h1>hello, {props.name}</h1>
}
  • 2022年之前 官方推荐方法,编写class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 1. 编写组件
class Welcome extends React.Component {
    // 重写render方法,该方法必须要实现,且需要有return语句
    render() {
        return <h1>Hello, {this.props.name}, {this.props.country}</h1>;
    }
}

// 2. 渲染组件. 组件的属性会传给this.props.
// 如下,Welcom的props = {name: "someBody", country: "China"}
const element = <Welcome name="someBody", country="China" />;

组件的注意事项:

  • 组件名必须大写
  • 组件的props为只读属性,不能被修改
  • 组件可以嵌套组件
  • 如果在组件中使用到局部变量,需要加关键字const
  • 组件分为有状态组件与无状态组件。建议无状态组件只关注UI渲染。有状态组件负责数据处理。单独分开

组件通信

  • Mobx

    终级大杀器,独立的一个库。简单好用

  • Context

    当多层级传递参数时,此为最优。提供了一个无需为每层组件手动添加props,就能在组件树间进行数据传递的方法。

    方法与步骤:

      1. 在提供context的组件内,新增一个getChildContext方法,返回context对象即可
      1. 组件的childContextTypes定义context对象的属性信息
    1
    
    
    
  • 父组件向子组件传递数据,直接通过调用子组件时,指定props

1
2
// name="someName"即为子组件Input的props数据
<Input name="someName">
  • 子组件向父组件传递数据,也通过props,在父组件中定义好函数,并将函数通过props传递给子组件,子组件在内部调用该函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 子组件
class ItemList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {newUser: ""};
    }

    handleChange = (e) => {
        this.setState({newUser: e.target.value});
    }

    handleClick = () => {
        if (! this.state.newUser == "") {
            this.props.AddUser(this.state.newUser);
        }
    }

    render() {
        return (
            <div>
                <ul>
                    {this.props.users.map(user => <li key={user}>{user}</li>)};
                </ul>
                <input name="newUser" value={this.state.newUser} onChange={this.handleChange}></input>
                <button onClick={this.handleClick}>新增用户</button>
            </div>
        )
    }
}

// 父组件
class ItemContain extends React.Component {
    constructor(props) {
        super(props);
        this.stae = {users: props.users};
    }

    addUser = (user) => {
        const preUsers = this.state.users;
        preUsers.push(user);
        this.setState({users: preUsers});
    }

    render() {
        {/*AddUser作为props传递给子组件,而后又在子组件中调用该函数*/}
        return <ItemList users={this.state.users} AddUser={this.addUser} />   
    }
}

// 再上一级调用父组件
<ItemContain users={["Alice", "Blob"]} />
  • 兄弟节点组件通信(状态提升)

    兄弟组件之间不能直接互相通信,需要通过状态提升的方式进行通信。也是同子组件同父组件通信一样,在父组件定义好函数,通过props传递给兄弟组件,在兄弟组件中调用

    • 兄弟组件内部不维护自己的state
    • 兄弟组件改变的状态其实是父组件的state,兄弟组件中将父的state进行渲染输出
    • 父组件将内部组件的值,通过props的方式传递给兄弟组件
    • 兄弟组件的onChange等事件处理函数实际调用的是父中的函数,也通过props传递给兄弟组件,在它内部的事件处理函数中调用父组件的函数

    示例:

    下面是人民币同美元的实时转换,这里简单起见,直接假设汇率固定为7,输入全部合法,实际中输入要判断是否合法的

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
    // 定义汇率
    const rate = 7;
    
    // 1. 定义父组件以及内部的state
    class Container extends React.Component {
        constructor(props) {
            super(props);
            // curr = y  人民币
            // curr = u  美元
            this.state = {value: "", curr: "y"}
        }
    
        // 给子组件调用的,转换成RMB
        changeToY = (newValue) => {
            this.setState({value: newValue, curr: "y"});
        }
    
         // 给子组件调用的,转换成美元
        changeToU = (newValue) => {
            this.setState({value: newValue, curr: "u"})
        }
    
        render() {
            // 计算一下,再将相关值传入子组件/兄弟组件
            const value = this.state.value;
            const curr = this.state.curr;
            const rmb = curr === "y" ? value : (value / rate);
            const dollar = curr === "u" ? value : (value * rate);
    
            return (
                <div>
                // 调用子组件
                Dollar: <ContainerInput name="y" handle={this.changeY} value={rmb} />
                RMB: <ContainerInput name="u" handle={this.changeU} value={dollar} />
                </div>
            )
        }
    }
    
    // 子组件
    class ContainerInput extends React.Component {
        constructor(props) {
            super(props);
            // 内部不需要保持状态,即不需要state
        }
    
        // 实际调用的是父的函数,通过props传进来
        onChange = (e) => {
            this.props.handle(e.target.value);
        }
    
        render() {
            return (
                // 这里不能直接使用this.props.handle函数,如最直接使用,参数e.target实际上undefined,所以还得用自身的函数再包装一下使用,才能取到e.target
                <input name={this.props.name} value={this.props.value} onChange={this.onChange}></input>
            )
        }
    }
    

State

使用步骤:

  1. 组件定义成class
  2. 重写方法constructor方法,并在该方法中初始化this.state

构造函数是唯一可以初始化this.state的地方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import PropTypes from 'prop-types'

class Welcom extends React.Component {
    constructor(props) {
        super(props)
        this.state = {name: "value", other: ""};
        // 复用props
        // this.state = {...props}
        // 将props的name属性修改为新的值。如props = {name: "myname"}
        // this.state = {name: "newName"}
        // this.state = {...props, name: "newName"}
    }
    
    // 给类添加属性为attrName,为普通属性
    attrName = "value"

    // 类似静态语言的类型注释,用于告诉props参数名与类型
    // static 为类属性
    static propTypes = {
        name: PropTypes.string,
        // 必须存在的值
        other: PropTypes.string.isRequired
    }

    // 指定props的默认值
    static defaultProps = {
        name: "defaultName",
        other: 123456
    }
    /*
    // 设置
    this.setState({name1: "newValue", name2: "value2",...})
    */
   // 其它方法略过
   ...
}
  • State用于内部变量,props用于外部变量,props为只读,不可变
  • class内的其他变量为普通属性
  • 如果在render方法中没有用到的属性,建设设置为普通属性,不要加在state

生命周期(2022之后不适用了,但了解下还是很有必要

以下为主要使用到的方法

  • constructor

    构造器,主要用于初始化this.state

  • componentWillReceiveProps

    一般用于props有变更进执行,但不常见,一般可以将props作为参数,同前面的props进行比较

  • componentDidMount

    在组件已经被渲染到DOM中后运行,所以,最好在这里设置计时器.

    • 一般当要实时更新内部的state属性时,在这设置计时器。要注意需要用箭头函数
    • 请求后端数据时,一般也在这里执行

    箭头函数主要用于实时更新的函数中。如通过定时器更新this.state的函数

  • componentWillUnmount

    一般用于生命周期方法中清除计时器

事件处理

e参数会作以参数隐式的传进来,如果处理函数有多个参数,e作为最后的参数

  • 语法上使用箭头函数形式绑定this
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class LoginButton extends React.Component {
    // 函数需要至少一个参数为e,就是事件,如最多个参数,e作为最后的参数
    handClick = (e) => {
        console.log("this is ", this);
        console.log("e is ", e);
    }

    render() {
        // 返回多选时,用中括号包起来
        return (
            // 调用时,不需要指定参数e,React会自动传入
            <button onClick={this.handClick}>
                Click Me
            </button>
        )
    }
}
  • 向事件函数传递多参数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class LoginButton extends React.Component {
    // 函数需要至少一个参数为e,就是事件,如最多个参数,e作为最后的参数
    handClick = (arg, e) => {
        console.log("this is ", this);
        console.log("arg is ", arg);
        console.log("e is ", e);
    }

    render() {
        // 返回多选时,用中括号包起来
        return (
            // 调用时,不需要指定参数e,React会自动传入
            <button onClick={this.handClick.bind(this, "arg")}>
                Click Me
            </button>
        )
    }
}
  • 修改state中的数组类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Liste extends React.Component {
    constrouctor(props) {
        super(props);
        this.state = {datas: props.initDatas};
    }

    handChange = (preState, e) {
        this.setState(
            {/*重点一: 使用箭头函数*/}
            preState => {dats: [...preState.datas, "NewItem"]}
        )
    }

    render() {
        return (
            {/*重点二: 使用bind,传入之前的state*/}
            <input onClick={this.handleChange.bind(this, this.state)}></input>
        )
    }
}
  • 事件处理函数不能返回false,需要通过e.preventDefault()执行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class LoginButton extends React.Component {
    clickHandle = (e) => {
        console.log("return false");
        e.preventDefault();
    }

    render() {
        return <button onClick={this.clickHandle}>Click Me</button>
    }
}

条件渲染

JSX的中括号中"{}"可以使用任意javascript表达式

阻止组件渲染

如果有时想有些组件在条件不满足情况下不渲染,可以直接返回null即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function button(props) {
    if !(props.display) {
        return null;
    }

    return (
        <div>Display thing</div>
    )
}

class Page extends React.Component {
    constructor(props) {
        super(props);
        this.state = {dispaly: true};
        // others
        // ...
    }

    render() {
        return (
            button(this.state);
            // other something
            // ....
        )
    }
}

列表

  • React中,列表需要指定key,一般map()方法中的元素需要设置key属性
  • key只有在兄弟中不唯一即可,可以全局不唯一
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class List extends React.Component {
    constructor(props) {
        super(props);
        // 简单起见,这里直接复制props
        this.state = JSON.parse(JSON.stringify(props));
    }

    // 注意map的箭头函数没用大括号,用了就不对
    render() {
        return(
            <ul>
                {this.state.dats.map((data) =>
                    <li key={data}>{data}</li> 
                )}
            </ul>
        )
    }
};

List.defaultProps = {
    datas: ["Python", "Golang", "Lua"],
    name: "unkonw"
}

表单

如果一个表单元素的值是由React来管理的,那么它就是一个受控组件

input/textarea

通过onChange事件与value结合,在组件初始化时,将state赋于表单属性名称,后续根据value来更新state.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Input extends React.Component {
    constructor(props) {
        super(props)
        // 初始化this.state.当key为变量时,需要加中括号
        this.state = {[props.name]: ""}
    }

    // onChange函数
    handleChange = (e) => {
        // 实时更新this.state
        this.setState({[e.target.name]: e.target.value});
    }

    render() {
        return (
            <input name={this.props.name} value={this.state[this.props.name]} onChange={this.handleChange}>
            </input>
        )
    }

}

// 使用Input组件,传入的username作为内部<input>中的name
<Input name="username"/>

select

类似Input,在父元素<select></select>中定义onChange属性即可

checkbox/radio

修改对应选项的checked属性即可

非受控组件(不推荐使用)

通过为表单元素使用特殊ref可以让非受控组件获取from的数据,而不用每个标签使用onChange函数来监听。

高阶组件

高阶组件实际就是装饰者模式,给组件添加功能。它本身是一个函数,传入的是函数,返回的也是函数。如

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 高阶组件(装饰函数,传入React.Componet组件
function withData(component) {
    // 返回包装后的component
    return class extends React.Componet {
        componetWillMount() {
            // 假设这里是一个Object
            const data = {key1: "value1", key2: "value2"};
            this.setState({data});
        }

        render() {
            // 原样返回原来的组件,只是为组件统一增加一个函数
            <component data={this.state.data} {...this.props}/>
        }
    }
}

// 被包装的组件
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <h1>name {this.props.name} </h1>
        )
    }
}

// 使用包装函数
const newComponent = withData(MyComponent);