React之上下文 & 样式私有化处理

29 阅读3分钟

一.context

1.创建上下文对象

使用React.createContext创建一个上下文对象

import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;

2.类组件中使用上下文

关键属性:Provider、Consumer、contextType

  1. 父组件中通过Provider传递上下文数据
import React from "react";
import VoteMain from './VoteMain';
import VoteFooter from './VoteFooter';
import ThemeContext from "@/ThemeContext";
import './Vote.less';

class Vote extends React.Component {
    state = {
        supNum: 10,
        oppNum: 5
    };
    change = type => {
        let { supNum, oppNum } = this.state;
        if (type === 'sup') {
            this.setState({ supNum: supNum + 1 });
            return;
        }
        this.setState({ oppNum: oppNum + 1 });
    };
    render() {
        let { supNum, oppNum } = this.state;
        return <ThemeContext.Provider
            value={{
                supNum,
                oppNum,
                change: this.change
            }}>
            <div className="vote-box">
                <div className="header">
                    <h2 className="title">React是很棒的前端框架</h2>
                    <span className="num">{supNum + oppNum}</span>
                </div>
                <VoteMain />
                <VoteFooter />
            </div>
        </ThemeContext.Provider>;
    }
}
export default Vote;
  1. 子组件中通过contextType使用上下文数据
import React from "react";
import ThemeContext from "@/ThemeContext";

class VoteMain extends React.Component {
    static contextType = ThemeContext;
    render() {
        let { supNum, oppNum } = this.context;
        return <div className="main">
            <p>支持人数:{supNum}人</p>
            <p>反对人数:{oppNum}人</p>
        </div>;
    }
}
export default VoteMain;

3.子组件中通过Consumer使用上下文数据

import React from "react";
import { Button } from "antd";
import ThemeContext from "@/ThemeContext";

class VoteFooter extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {(context) => {
          let { change } = context;
          return (
            <div className="footer">
              <Button type="primary" onClick={change.bind(null, "sup")}>
                支持
              </Button>
              <Button type="primary" danger onClick={change.bind(null, "opp")}>
                反对
              </Button>
            </div>
          );
        }}
      </ThemeContext.Consumer>
    );
  }
}
export default VoteFooter;

3.函数组件中使用上下文

关键属性:Provider、Consumer、useContext

1.父组件中传递数据

import React, { useState } from "react";
import VoteMain from './VoteMain';
import VoteFooter from './VoteFooter';
import ThemeContext from "../ThemeContext";
import './Vote.less';

const Vote = function Vote() {
    let [supNum, setSupNum] = useState(10),
        [oppNum, setOppNum] = useState(5);
    const change = type => {
        if (type === 'sup') {
            setSupNum(supNum + 1);
            return;
        }
        setOppNum(oppNum + 1);
    };
    return <ThemeContext.Provider
        value={{
            supNum,
            oppNum,
            change
        }}>
        <div className="vote-box">
            <div className="header">
                <h2 className="title">React是很棒的前端框架</h2>
                <span className="num">{supNum + oppNum}</span>
            </div>
            <VoteMain />
            <VoteFooter />
        </div>
    </ThemeContext.Provider>;
};
export default Vote;

2.子组件通过useContext使用上下文数据

import React, { useContext } from "react";
import { Button } from 'antd';
import ThemeContext from "../ThemeContext";

const VoteFooter = function VoteFooter() {
    let { change } = useContext(ThemeContext);
    return <div className="footer">
        <Button type="primary" onClick={change.bind(null, 'sup')}>支持</Button>
        <Button type="primary" danger onClick={change.bind(null, 'opp')}>反对</Button>
    </div>;
};
export default VoteFooter;
  1. 子组件通过Consumer使用上下文数据
import React, { useContext } from "react";
import { Consumer } from "../ThemeContext";

const VoteMain = function VoteMain() {
  return (
    <Consumer>
      {(context) => {
        let { supNum, oppNum } = context;
        return (
          <div className="main">
            <p>支持人数:{supNum}人</p>
            <p>反对人数:{oppNum}人</p>
          </div>
        );
      }}
    </Consumer>
  );
};
export default VoteMain;

二.样式私有化

1.内联样式

通过内联样式解决样式冲突

缺点:都写在行内,不美观,冗余高,维护难

使用场景:偶尔可以对一些特定样式这样写

2.命名规范

对className有严格的命名规范!

项目中一般是以文件名 + 类名方式;

UI组件库一般采用BEM命名规范:block块-element元素-Modifier修饰符:el-button_default

3.CSSModules

以xxx.module.css格式的文件,会被默认编译成对象!react脚手架安装了一个插件帮我们做的!

Menu.module.css

.box {
    background: lightpink;
}

.box .list {
    font-size: 14px;
}

Menu.jsx

import React from "react";
import sty from "./Menu.module.css";

const Menu = function Menu() {
  return (
    <div className={sty.box}>
      <ul className={sty.list}>
        <li>电脑</li>
        <li>家电</li>
      </ul>
    </div>
  );
};
export default Menu;

4.react-jss

使用react-jss插件,在jsx中编写css样式

样式编写

import React from "react";
import { createUseStyles } from 'react-jss';

export const useStyles = createUseStyles({
    box: {
        backgroundColor: 'lightblue',
        width: '300px'
    },
    title: {
        fontSize: '20px',
        color: 'red',
        '&:hover': {
            color: props => props.color
        }
    },
    list: props => {
        return {
            '& a': {
                fontSize: props.size + 'px',
                color: '#000'
            }
        };
    }
});

样式使用

import useStyles from './xxx.js'

const Nav = function Nav() {
    let { box, title, list } = useStyles({
        size: 14,
        color: 'orange'
    });
    return <nav className={box}>
        <h2 className={title}>购物商城</h2>
        <div className={list}>
            <a href="">首页</a>
            <a href="">秒杀</a>
            <a href="">我的</a>
        </div>
    </nav>;
};
export default Nav;

5.style-components

使用style-components插件,在js中编写css,vscode也有同名的代码提示插件

navStyle.js

import styled from "styled-components";
import { colorRed, colorBlue, titleSize } from './common';

/*
 基于 “styled.标签名” 这种方式编写需要的样式
   + 样式要写在“ES6模板字符串”中
   + 返回并且导出的结果是一个自定义组件

 如果编写样式的时候没有提示,我们可以在vscode中安装一个官方插件:vscode-styled-components
 */
export const NavBox = styled.nav`
    background-color: lightblue;
    width: 300px;

    .title{
        font-size: ${titleSize};
        color: ${colorRed};
        line-height: 40px;

        &:hover{
            color: ${colorBlue};
        }
    }
`;

export const NavBarBox = styled.div.attrs(props => {
  return {
    size: props.size || 16
  }
})`
    line-height: 40px;

    a{
        font-size: ${props => props.size}px;
        color: #000;
        margin-right: 10px;

        &:hover{
            color: ${props => props.hover};
        }
    }
`;

组件中使用

import React from "react";
import { NavBox, NavBarBox } from './NavStyle';

const Nav = function Nav() {
    return <NavBox>
        <h2 className="title">购物商城</h2>
        <NavBarBox hover="#ffe58f">
            <a href="/home">首页</a>
            <a href="/rush">秒杀</a>
            <a href="/my">我的</a>
        </NavBarBox>
    </NavBox>;
};
export default Nav;