深入浅出React-样式处理及组件通信

571 阅读3分钟

React中处理样式的五种方法

行内样式

const styleApp = {
    fontSize: 20,
    color: 'red',
}

export default class App extends React.Component {
    render() {
        return (
            <h1 style={ styleApp }>首页面</h1>
        )
    }
}

note

  • 行内样式使用对象赋值

外部样式

新建一个CSS文件, 在JS文件中引入 CSS文件

import "./App.css"

CSS的模块化

  • CSS的模块化命名必须是 xxx.module.css
  • xxx.module.css 中的代码如下:
.error{
    color: red;
    font-weight: bold;
}

使用:

  • 引入import common from "./css/common.module.css"

  • 使用 <h1 className={ common.error }>错误信息

根据不同的条件添加不同的样式

这里我们使用 classnames 这个包

  • 安装 npm i classnames

  • 引入 : import classNames from 'classnames';

  • 举个例子

import React, { Component } from 'react'
import classNames from "classnames"

// 引入外部样式
import "./App.css"

export default class App extends Component {
    render() {
        let h1Class = classNames({
            'box1':true,
            'box2':true
        })
        return (
           <div className="container">
               <h1 className={ h1Class }>这是一个盒子</h1>
           </div>
        )
    }
}

styled-components & css in js

安装: npm i styled-components -S
所谓 css in js, 就是在 js 中写css.

  1. 创建一个 style.js 文件:
 import styled from "styled-components"

// 创建一个 Container 组件,它将渲染一个附加了样式的 <div> 标签
let Container = styled.div`
    width:500px;
    height:500px;
    background:${ (props)=>props.color  };
    font-size:30px;
    h1{
        color:blue
    }
`

export {
    Container
}
  1. 在需要使用的地方先引入该组件,然后当成标签使用,代码如下:
import React, { Component } from 'react'
import { Container } from "./style"
export default class Search extends Component {
    render() {
        return (
            <Container color='gold'>
                <h1>Helo React~</h1>
            </Container>
        )
    }
}

组件间通信

React是以组件组合的形式组织的。所以在嵌套关系上,就会有四种不同的可能性:

  • 父组件向子组件通信
  • 子组件向父组件通信
  • 没有嵌套关系的组件通信
  • 跨级组件通信

父组件向子组件通信

class ListItem extends Component {
    constructor(props) {
        super(props);
        this.value = this.props.value;
    }
    render() {
        return (
            <li>
                <span>{this.value}</span>
            </li>
        )
    }
}
export default class App extends Component {
    render() {
        const list = [1, 2, 3, 4, 5];
        return (
            <ul>
                {list.map((entry, index) => (
                    <ListItem key={`list-${index}`} value={entry}/>
                ))}
            </ul>
        )
    }
}

子组件向父组件通信

在用React之前的组件开发模式时,常常需要接收组件运行时的状态,这时我们的方法有:

  • 利用回调函数
父组件将一个函数作为props传递给子组件,子组件调用该回调函数,便可以向父组件通信
  • 利用自定义事件机制 举个例子
class Child extends Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: '子组件的msg传值'
        }
    }
    // 通过props 调用回调函数传值
    trans = () => {
        this.props.callback(this.state.msg);
    }
    render() {
        return (
            <div>
                <button onClick={this.trans}>传值给父组件</button>
            </div>
        )
    }
}

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: '父组件初始msg'
        }
    }

    // 父组件回调函数,更新state,进而更新父组件
    callback = (msg) => {
        console.log(msg);
        // setState方法,修改msg参数,值是由子组件传过来的
        // this.setState({ msg })
    }

    render() {
        return (
            <div className="App">
                <p>{this.state.msg}</p>
                <Child callback={this.callback}/>
            </div>
        )
    }
}

没有嵌套关系的组件通信

我们就常以 发布/订阅模式来举例子, 这里我们用 node.js Events模块来实现。

好像就是vue中的EventBus方法

note 我们在处理事件的过程中需要注意, componentDidMount 事件中, 如果组件挂载完成, 再订阅事件; 当组件卸载的时候, 在componentWillUnmount 事件中取消事件的订阅。

安装events并引入

  1. npm i events --save
  2. 新建 Evt.js 导入 events
import { EventEmitter } from 'events'
export default new EventEmitter();

具体是这样的

import React, { Component } from 'react';
// import PropTypes from 'prop-types';
import emitter from './Evt.js';

export default class App extends Component {
    handleClick = () => {
        emitter.emit('call', 'hello world');
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                <Custom1/>
            </div>
        )
    }
}

class Custom1 extends Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: ''
        }
    }

    componentDidMount() {
        emitter.addListener('call', (msg) => {
            this.setState({ msg })
        })
        // emitter.removeListener('call');
    }

    componentWillUnmount() {
        emitter.removeListener('call');
    }

    render() {
        return (
            <p style={{color:'red'}}>
                {this.state.msg}
            </p>
        )
    }
}

跨级组件通信

所谓跨级组件通信,就是父组件向子组件的子组件通信, 向更深层的子组件通信。一般来说有以下两种方法:

  • 中间组件层层传递props
  • 使用context对象

使用context需要满足两个条件:

  • 上级组件要声明自己支持 context, 并提供一个函数来返回相应的 context对象
  • 子组件要声明自己需要使用 context

下面我们举个例子, 新建三个文件:

  • App.js 父组件
  • Sub.js 子组件
  • SubSub.js 子组件的子组件

App.js

import React, { Component } from 'react';
import PropTypes from "prop-types";
import Sub from "./Sub";
import "./App.css";

export default class App extends Component{
    // 父组件声明自己支持 context
    static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    // 父组件提供一个函数,用来返回相应的 context 对象
    getChildContext(){
        return{
            color:"red",
            callback:this.callback.bind(this)
        }
    }

    callback(msg){
        console.log(msg)
    }

    render(){
        return(
            <div>
                <Sub></Sub>
            </div>
        );
    }
} 

Sub.js

import React from "react";
import SubSub from "./SubSub";

const Sub = (props) =>{
    return(
        <div>
            <SubSub />
        </div>
    );
}

export default Sub;

SubSub.js

import React,{ Component } from "react";
import PropTypes from "prop-types";

export default class SubSub extends Component{
    // 子组件声明自己需要使用 context
    static contextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }
    render(){
        const style = { color:this.context.color }
        const cb = (msg) => {
            return () => {
                this.context.callback(msg);
            }
        }
        return(
            <div style = { style }>
                SUBSUB
                <button onClick = { cb("我胡汉三又回来了!") }>点击我</button>
            </div>
        );
    }
}

有几点需要注意:

  • 父组件需要声明自己支持 context,并提供context中属性的 PropTypes
static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }
  • 子组件需要声明自己需要使用 context,并提供其需要使用的 context 属性的 PropTypes
static contextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }
  • 父组件需提供一个 getChildContext 函数,以返回一个初始的 context 对象