react 学习之路

第一天,安装脚手架。

1. npx create-react-app your-app 

第二天。开始学习

1.页面输出一个 hello

src 文件下 index.js 入口文件编写。

import React from 'react';
import ReactDOM from 'react-dom';  // 作用对象浏览器
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />  // 类的实例化
  </React.StrictMode>,
  document.getElementById('root')
)

src 文件下创建 App.js

import React from "react";

// es6 创建一个类,继承自React.Component
class App extends React.Component {
  render () {
    return (
      <div>hello</div>
    )
  }
}

export default App

2. 定义一个函数式组件,输出hello world

src 文件夹下 index.js

import React from 'react';
import ReactDOM from 'react-dom';
// import App from './App';

const App = <div>hello world</div>
ReactDOM.render(
    App,
  document.getElementById('root')
)

如果要传参数 ,定义一个函数 (函数式组件的雏形)

import React from 'react';
import ReactDOM from 'react-dom';
// import App from './App';

const App = (props) => {    // props 接收参数
 return <div>hello world{props}</div>
}
ReactDOM.render(
    App('!!!'), // 传3个感叹号
  document.getElementById('root')
)

 这里我们定义的方法实际上也是react定义组件的第一种方式-定义函数式组件,这也是无状态组件。

import React from 'react';
import ReactDOM from 'react-dom';
// import App from './App';

const App = (props) => {    // props 接收参数
  console.log(props, typeof(props))
  let {title} = props  // 因为 title 是一个对象,所以需要解构
 return <div>hello world{title}</div>
}
ReactDOM.render(
    <App title="!"></App>,
    document.getElementById('root')
)

 3.定义一个类组件

传入的是一个类,react 解构:

class App extends React.Component {
  render () {
    return (
      <div>hello {this.props.title}</div>
    )
  }
}
ReactDOM.render(
    <App title="!!!"></App>,
    document.getElementById('root')
)

传入的是一个实例:

class App extends React.Component {
  render (props) {
    return (
      <div>hello {props}</div>
    )
  }
}
const app = new App()

ReactDOM.render(
    app.render('...'),
    document.getElementById('root')
)

4.组件嵌套

// 类组件
import React, {Fragment} from "react";
// 创建两个类组件
class Header extends React.Component {
    render() {
        return (
            <div>header</div>
        )
    }
}
class Content extends React.Component {
    render () {
        return (
            <div>content</div>
        )
    }
}
// 组件嵌套
export default class App extends React.Component {
    render () {
        return (
            <Fragment>
                <Header></Header>
                <Content></Content>
            </Fragment>
        )
    }
}

Fragment 类似于vue 中的 template,占位的作用,一个组件下必须要有一个根节点。但是它可以简写。

<>
    <Header></Header>
    <Content></Content>
</>

也可以这样写,因为前面我们已经定义了两个组件。

createElement(
                Fragment,
                null,
                createElement(
                    Header
                ),
                createElement(
                    Content
                )
            )

 如果前面没有定义组件,那么可以这样,但是这样写太麻烦了。

createElement(
                Fragment,
                null,
                createElement(
                    'div',
                    {
                        title:'myclass'
                    },
                    '我的属性',
                    createElement(
                        'p',
                        {
                            title:'class_children'
                        },
                        '我的属性的子元素'
                    )
                )
            )

 5.js 中书写 css 样式

1.动态样式

import React from "react";
import { Component } from "react/cjs/react.production.min";
const styles = {
    fontSize:'36px',
    color:'red'
}
class ClassStyle extends Component {
    render () {
        return (
            <div style={styles}>样式</div>
        )
    }
}
export default ClassStyle

 

 2.静态样式

创建一个 .css 文件,在这里面书写样式,然后引入。

例如:创建一个style.css 

 在  js 文件中引入

import React from "react";
import { Component } from "react/cjs/react.production.min";
import './style.css'  // 引入刚刚写好的样式
class ClassStyle extends Component {
    render () {
        return (
            <div className="font">外部引入样式</div>
        )
    }
}
export default ClassStyle

效果:

 3.第三方库写样式 classname 不同的条件添加不同的样式
有时候需要根据不同的条件添加不同的样式,比如:完成状态,完成是绿色,未完成状态是灰色

npm  install classnames --save

 

 4. css-in-js
styled-components是针对React写的一套css-in-js框架,官网有详细介绍如何使用

npm install styled-components --save

示例:新建一个styledcss.js 文件 ,导入 styled-components,然后开始写样式。

 第三天:组件的挂载方式

 1. 父传子

创建一个父组件,导入子组件,传递一个数据 title="parent"

import React from "react";
import Child from "./Child";
class DataMount extends React.Component {
    render () {
        return (
            <>
            <div>Data</div>
            <Child title="parent"></Child>
            </>
        )
    }
}
export default DataMount

创建一个子组件:接收父组件传过来的数据,this.props.title

import React from "react";

class Child extends React.Component {
    render () {
        return (
            <div> child{this.props.title} </div>
        )
    }
}
export default Child

如果只是为了渲染数据,不包含复杂的逻辑计算,那么上面的子组件可以写成函数式组件,提高内存效率。

import React from "react";
// 函数式组件 无状态组件
export default (props) => {
    return (
        <div>child {props.title}</div>
    )
}

2.给组件设置默认值(给类加一个静态属性)

import React from "react";

class Child extends React.Component {
    // 给类加一个静态属性
    static defaultProps = {
        title : 'default value'
    }
    render () {
        return (
            <div> child{this.props.title} </div>
        )
    }
}
export default Child

 当父组件没有给子组件传递值时,才会使用默认值。

静态属性也可以直接通过子组件调用赋值

Child.defaultProps = {
    title: 'default value02'
}

上面是用类组件的写法,那么上面的组件写成函数式组件就是:

export default function  Child(props) {
    return (
        <div>child {props.title}</div>
    )
}
Child.defaultProps = {
    title: 'default value03'
}

3. props. children

类似于 vue 中的slot

 4. 使用prop-types检查props

更多用法前往官网:prop-types - npm

npm install --save prop-types

import React from "react";
import PropTypes from "prop-types";

class Child extends React.Component {
    static propTypes = {
        title: PropTypes.string
    }
    render () {
        return (
            <div>child {this.props.title}</div>
        )
    }
}


export default class Parent extends React.Component {
    render () {
        return (
            <div>
                <Child title=' I give you a string'></Child>
            </div>
        )
    }
}

如果传入的是一个数字

 

 例如它还可以渲染一个组件:

 要先实例化

在上面的 Child.propTypes修改 ,然后创建一个组件

 5.state

第一种方式,构造函数

isShow 为 true 时显示这个 div, isShow为false 就不显示.

 第二种方式,state属性

 6.setState

3s 以后这个div 就消失了

componentDidMount () {
        setTimeout(() => {
            this.setState(prevState => {
                return {
                    isShow: !prevState.isShow
                }
            })
        }, 3000);
    }

状态改变,DOM更新完了以后传递一个回调函数

 tips:

当直接改变state 中的状态时,例如向一个列表中 push 一个值,需要再次调用 setState({})告诉页面需要更新。

this.state.list.push('d)

this.setState({})

3、属性vs状态


相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
1.属性能从父组件获取,状态不能
2.属性可以由父组件修改,状态不能
3.属性能在内部设置默认值,状态也可以
4.属性不在组件内部修改,状态要改
5.属性能设置子组件初始值,状态不可以
6.属性可以修改子组件的值,状态不可以
state的主要作用是用于组件保存、控制、修改自己的可变状态。state在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为state是一个局部的、只能被组件自身控制的数据源。state中状态可以通过this.setstate方法进行更新,setstate会导致组件的重新渲染。
props的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的props,否则组件的props永远保持不变。
如果搞不清 state和props的使用场景,记住一个简单的规则:尽量少地用state,多用props。
没有 state的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful
component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。
 

第四天

受控组件

import React from "react";
class Control extends React.Component {
    state = {
        textValue: 'hello'
    }
    handleChange(e){
        this.setState({
            textValue:e.target.value
        })
    }
    render () {
        return (
            <div>
                <input 
                value = {this.state.textValue} type="text" 
                onChange={this.handleChange.bind(this)} />
            </div>
        )
    }
}
export default Control

非受控组件

import React, { createRef } from "react";
class Control extends React.Component {
    constructor(props){
        super(props)
        this.ipt = createRef()
    }
    state = {
        textValue: 'hello'
    }
    handleChange(e){
        this.setState({
            textValue:e.target.value
        })
    }
    render () {
        return (
            <div>
                <input type="text" onInput={this.handleChange.bind(this)} ref={this.ipt}/>
            </div>
        )
    }
    componentDidMount() {
        setTimeout(()=> {
            console.log(this.state.textValue)
        },3000)
    }
}
export default Control

渲染数据

import React from "react";
const data = '<h1>hello</h1>'
export default class XuanRan extends React.Component {
    render(){
        return (
            <div>
                <div dangerouslySetInnerHTML={{__html:data}}></div>
                <label htmlFor="abc"> 选择
                <input type="email" />
                <input type="text" />
                <input type="checkbox" id="abc"/>
                </label>
            </div>
        )
    }

}

短路运算符

当为 true 时,Home 内容渲染出来,当为false 不渲染。

import React from "react";
const Home = () => {
    return (
        <div>home</div>
    )
}
export default class XuanRan extends React.Component {
    render(){
        return (
            <div>
                {true && <Home></Home>}
            </div>
        )
    }

}

事件处理


1、绑定事件


采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick,
React里的事件是驼峰onClick,React的事件并不是原生事件,而是合成事件。


2、事件handler的写法


·直接在render里写行内的箭头函数(不推荐)
·在组件内使用箭头函数定义一个方法(推荐)
·直接在组件内定义一个非箭头函数的方法,然后在render里直接使用οnclick=
{this.handleclick.bind(this)}(不推荐)
·直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)


3、Event对象


和普通浏览器一样,事件handler会被自动传入一个 event对象,这个对象和普通的浏览器 event对象所包含的方法和属性都基本一致。不同的是React中的event对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagation、event.preventDefault这种常用的方法


4、事件的参数传递


·在render里调用方法的地方外面包一层箭头函数
在render里通过this.handleEvent.bind(this,参数)这样的方式来传递
·通过event传递
·比较推荐的是做一个子组件,在父组件中定义方法,通过props传递到子组件中,然后在子组件件通过this.props.method来调用

1.一个简单的点击事件

import React from "react";
export default class Event extends React.Component {
    handleClick() {
        console.log('Click.')
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick}>Click</button>
            </div>
        )
    }
}

2. 方法访问类里面的 state

es6 类里面的函数访问 this 是拿不到的。

import React from "react";
export default class Event extends React.Component {
    state = {
        count: 0
    }
    handleClick() {
        console.log(this.state.count)
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick}>Click</button>
            </div>
        )
    }
}

3. 但在jsx 模板上可以访问到,但是在方法里面访问不到。通常的做法是在 jsx 模板里添加 bind

<button onClick={this.handleClick.bind(this)}>Click</button>

 这样就可以访问了。

4.但是每点击一次就要 bind 一次,性能比较差,于是优化一下

import React from "react";
export default class Event extends React.Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
    }
    state = {
        count: 0
    }
    handleClick() {
        console.log(this.state.count)
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick}>Click</button>
            </div>
        )
    }
}

5.事件要传参怎么办?定义一个箭头函数

import React from "react";
export default class Event extends React.Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
    }
    state = {
        count: 0
    }
    handleClick(args) {
        console.log(args)
    }
    render(){
        return (
            <div>
                <button onClick={() => this.handleClick('abcd') }>Click</button>
            </div>
        )
    }
}

6.定义一个函数,返回一个函数,方便传参

import React from "react";
export default class Event extends React.Component {
    constructor(props) {
        super(props)
        this.handleClick = this.handleClick.bind(this)
    }
    state = {
        count: 0
    }
    handleClick = (args) => {
        return () => {
            console.log(args)
        }
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick('abc')}>Click</button>
            </div>
        )
    }
}

7. 点击以后触发一个函数 handleClick 返回一个函数  return,在这里还可以获取到事件

return (event) => {
            console.log(event)

 表单单选

import React from "react";
export default class Form1 extends React.Component {
    state = {
        textvalue:'',
        textareaValue:'' ,
        seclectvalue:['0','1']
    }
    handleSubmit =  () => {
        return (e) => {
            e.preventDefault()
            console.log(this.state.textvalue)
            console.log(this.state.textareaValue)
            console.log(this.state.seclectvalue)
        }
    }
    handleChange = () => {
        return (e) => {
            this.setState({
                textvalue:e.target.value
            })
        }
    }
    handleAreaChange = () => {
        return (e) => {
            this.setState({
                textareaValue:e.target.value
            })
        }
    }
    handleSelectChange= ()=>{
        return (e) => {
            this.setState({
                seclectvalue:e.target.value
            })
        }
    }
    render () {
        return (
            <div>
                <form onSubmit={this.handleSubmit()}>
                    <input type="text" 
                    value={this.state.textvalue} 
                    onChange={this.handleChange()} />
                    <br />
                    <textarea name="" id="" cols="30" rows="10" 
                    value={this.state.textareaValue}
                    onChange={this.handleAreaChange()}></textarea>
                    <br />
                    <select 
                    multiple
                    value={this.state.seclectvalue}
                    onChange={this.handleSelectChange()}>
                        <option value="0">成都</option>
                        <option value="1">咸阳</option>
                        <option value="2">洛水</option>
                    </select>
                    <br />
                    <input type="submit" value="提交" />
                </form>
            </div>
        )
    }
}

表单多选:

import React from "react";

export default class MuliteForm extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value:"cheng_zi",
            arr:[],
            options:[
                {value:"cheng_zi",lable:'橙子'},
                {value:"you_zi",lable:'柚子'},
                {value:"mang_guo",lable:'芒果'},
                {value:"apple",lable:'苹果'}
            ]
        }
        this.handleChange = this.handleChange.bind(this)
    }
    handleChange(e){
        // 查找数组中 如果有 item = e.target.vaule 就返回这个item 的索引
        //(即:当前数组中如果已经存在该选项了,那么要标记出来,记录索引,在下一步中把它删掉。这是为了避免重复选择)
        let index_1 = this.state.arr.findIndex(item => {
            return item === e.target.value
        })
        if (index_1 >= 0) {
            this.state.arr.splice(index_1,1)
        }else {
            this.state.arr.push(e.target.value)
        }
        let arr = this.state.arr
        this.setState({arr})
    }
    handleClick = () => {
        return(e) => {
            console.log(this.state.arr)
        }
    }
    render () {
        return (
            <div>
                <select 
                multiple={true}
                value={this.state.arr}
                onChange={this.handleChange}>
                    {/* 遍历渲染 option */}
                    {this.state.options.map((item,index) => {
                        return <option value={item.value} key={index}>{item.lable}</option>
                    })} 
                </select>
                <button onClick={this.handleClick()}>get_value</button>
            </div>
        )
    }
}

表单多个输入框

import React from "react";

export default class Reservation extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        isGoing: true,
        numberOfGuests: 0
      };
  
      this.handleInputChange = this.handleInputChange.bind(this);
    }
  
    handleInputChange(event) {
      const target = event.target;
      const value = target.type === 'checkbox' ? target.checked : target.value;
      const name = target.name;
  
      this.setState({
        [name]: value
      });
    }
    handleClick = () => {
        return () => {
            console.log(this.state.isGoing)
            console.log(this.state.numberOfGuests)
        }
    }
  
    render() {
      return (
        <div>
          <div>
            参与:
            <input
              name="isGoing"
              type="checkbox"
              checked={this.state.isGoing}
              onChange={this.handleInputChange} />
          </div>
          <br />
          <div>
            来宾人数:
            <input
              name="numberOfGuests"
              type="number"
              value={this.state.numberOfGuests}
              onChange={this.handleInputChange} />
          </div>
          <div>
              <button onClick={this.handleClick()}>click</button>
          </div>
        </div>
      );
    }
  }

输入框表单(姓名)

import React from "react";
export default class NameForm extends React.Component {
    constructor(props){
        super(props)
        this.input = React.createRef()
        this.handleSubmit = this.handleSubmit.bind(this)
    }
    handleSubmit (e) {
        e.preventDefault()
        alert('你的名字是:'+ this.input.current.value)
        console.log(this.input)
    }
    render () {
        return (
            <form onSubmit={this.handleSubmit}>
                Nmae:
                <input type="text" ref={this.input}/>
                <input type="submit" value="Submit" />
            </form>
        )
    }
}

文件表单

获取文件名

import React from "react";

export default class FileForm extends React.Component {
    constructor(props) {
        super(props)
        this.file = React.createRef()
        
    }
    handleClick = () => {
        return () => {
            console.log(this.file.current.files[0].name)
        }
    }
    render () {
        return (
            <div>
                <input type="file" ref={this.file} />
                <button onClick={this.handleClick()}>get_filename</button>
            </div>
        )
    }
}

组件的生命周期 


React中组件也有生命周期,也就是说也有很多钩子函数供我们使用,组件的生命周期,我们会分为四个阶段,初始化、运行中、销毁、错误处理


1、初始化


在组件初始化阶段会执行
1. constructor
2. static getDerivedStateFromProps()
3. componentWillMount()/ UNSAFE_componentWillMount()
4. render()
5. componentDidMount()


2、更新阶段


props或state的改变可能会引起组件的更新,组件重新渲染的过程中会调用以下方法:
1. componentWillReceiveProps()/ UNSAFE_componentWillReceiveProps()
2. static getDerivedStateFromProps()
3. shouldComponentUpdate()
4. componentWillUpdate()/ UNSAFE_componentWillUpdate()
5. render()
6. getSnapshotBeforeUpdate()
7. componentDidUpdate()
 

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps会在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新state,如果返回nul1则不更新任何内容。

React的16.3版本中对生命周期进行了较大的调整,这是为了开发者能正确地使用生命周期,避免误解其概念而造成反模式。
要注意的是,React 16.3的版本中getDerivedStateFromProps的触发范围是和16.4へ是不同的,主要区别是在 setstate和forceupdate时会不会触发,具体可以看这个生命全周期图。
可能的使用场景有两个:
无条件的根据prop来更新内部state,也就是只要有传入prop值,就更新state
·只有prop值和state 值不同时才更新state值。

案例:

反面:子组件可以获取到父组件传过来的 state ,当父组件修改 state 时,子组件更新 ,获取到父组件修改了的 state。

定义一个父组件  father,起始 state 中的 color 为 blue , 2 s 以后修改 color 为 green,把color 传递给子组件 child

import React from "react";
import Child1 from "./Child1";
export default class LifeTime extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            color:'blue'
        }
    }
    render () {
        return (
            <div>
                Father1:{this.state.color}
                <Child1 color={this.state.color}></Child1>
            </div>
        )
    }
    componentDidMount () {
        setTimeout(() => {
            this.setState({
                color:'green'
            })
        },2000)
    }
}

child: 子组件本身有一个 state color 为 空,子组件从父组件拿到传递过来的color.

因为父组件的每一次 state 发生变化时,子组件getDerivedStateFromProps都会触发更新state,所以这里加了一个判断.只判断 color 有没有变化。更新前color 如果和更新后的color 相等,则返回 null.

import React from "react";
import { PureComponent } from "react/cjs/react.production.min";
export default class Child1 extends PureComponent {
    state = {
        color:'',
    }
    static getDerivedStateFromProps(nextprops, prevstate){
        console.log(nextprops.color,prevstate.color)
        if (nextprops.color === prevstate.color) {
            return null
        } else {
            return {
                color:nextprops.color
            }
        }
    }
    render () {
        return (
            <div>
                Child1_color:{this.state.color}
            </div>
        )
    }
    componentDidMount () {
        setTimeout(() => {
            this.setState({
                color:'red'
            })
        },4000)
    }
}

但是子组件修改自己的 color 时,却没有修改成功。因为 color 始终等于 nextprops.color

 改进:

只需要在子组件中存储一个color 冗余保存父组件传递过来的 color ,与当前color 进行比较

import React from "react";
import { PureComponent } from "react/cjs/react.production.min";
export default class Child1 extends PureComponent {
    state = {
        color:'',
        // 存储父组件传过来的 color
        preColor:''
    }
    static getDerivedStateFromProps(nextprops, prevstate){
        console.log(nextprops.color,prevstate.color)
        if (nextprops.color === prevstate.preColor) {
            return null
        } else {
            return {
                color:nextprops.color,
                preColor:nextprops.color
            }
        }
    }
    render () {
        return (
            <div>
                Child1_color:{this.state.color}
            </div>
        )
    }
    componentDidMount () {
        setTimeout(() => {
            this.setState({
                color:'red'
            })
        },4000)
    }
}

context 学习 

count.jsx 中 state 的 current 为 0.并且有两个方法 increment  decrement 实现 current 的加减。

child.jsx 和childhood.jsx 都可以获取 count.jsx 中的 current

childhood.jsx 通过方法 increment  decrement 传值给 count.jsx

 count.jsx 接收 childhood.jsx 传递过来的值进行计算改变 state 中的 current

child.jsx 和childhood.jsx 获取到新的 current

childhood.jsx 组件 在child.jsx 组件中

 

count2.jsx

import React from "react";
import Child from "./Child";
import {CountProvider} from './Count'
export default class Count2 extends React.Component {
    render () {
        return (
            <div>
                Count2 这是出口
                <br />
                <CountProvider>
                    <Child></Child>
                </CountProvider>
            </div>
        )
    }
}

 Count.jsx

import React from "react";
// 将 Provider , Consumer(取了一个别名: CountConsumer )从 React.createContext() 解构出来
const {Provider, Consumer:CountConsumer} = React.createContext()

 class CountProvider extends React.Component {
     constructor(props){
         super(props)
         this.increment =this.increment.bind(this)
         this.decrement = this.decrement.bind(this)
     }
    state = {
        count:0
    }
    increment (args) {
        this.setState(state => ({
            count:state.count + args
            })
        )
    }
    decrement (args) {
        this.setState(state => ({
            count:state.count - args
            })
        )
    }
    render () {
        console.log(this.props.children)
        return (
            // Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。
            // 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
            <Provider value={{
                count:this.state.count,
                increment:this.increment,
                decrement:this.decrement
            }}>
                Count
                {this.props.children}
            </Provider>
        )
    }
}
export {
    CountProvider,
    CountConsumer
}

child.jsx

import React from "react";
import ChildHood from "./ChildHood";
import { CountConsumer } from "./Count";
export default class Child extends React.Component {
    render () {
        return (
            <div>
                child
                {/* 这种方法需要一个函数作为子元素({value =>  基于 context 值进行渲染 })。
                这个函数接收当前的 context 值,并返回一个 React 节点。
                传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。
                 */}
                <CountConsumer>
                    {
                        ({count}) => <div>{count}</div>
                    }
                </CountConsumer>
                <ChildHood></ChildHood>
            </div>
        )
    }
}

childhood.jsx

import React from "react";
import { CountConsumer } from "./Count";
import {Consumer as ShowConsumer} from './ShowContext'
export default class ChildHood extends React.Component {
    render () {
        return (
            <div>
                childhood
                <CountConsumer>
                    {
                        ({count,increment,decrement}) => {
                            return (
                                <>
                                <div>{count}</div>
                                <div>
                                <button onClick={() => increment(3)}>+</button>
                                </div>
                                <ShowConsumer>
                                {
                                    (value) => (
                                        <div>{count}/ {value.show}</div>
                                    )
                                }
                                </ShowConsumer>
                                <div>
                                <button onClick={() => decrement(2)}>-</button>
                                </div>
                                </>
                            )
                        }
                    }
                </CountConsumer>
    
            </div>
        )
    }
}

showContext.js

import { createContext } from "react";
let {Provider,Consumer} = createContext({show:'hello'})
export {
    Provider,
    Consumer
}

testcontext.js

import { createContext } from "react";

const testContext = createContext()
const {Provider,Consumer}  = testContext
export {
    testContext,
    Provider,
    Consumer
}

 高阶组件

一般我们把增强这个组件的函数叫做高阶组件

hoc.js

import React from "react"

// 可以接收一个组件作为参数,并返回一个组件
const hoc = (Comp) => {
    return class extends React.Component {
        render() {
            return (
                <Comp title="hello" {...this.props}></Comp>
            )
        }
        componentDidMount() {
            // App组件
            console.log(this)
        }
    }
}
export default hoc
// hoc 是高阶组件

App.jsx

import React from "react";
import hoc from "./hoc";
class App extends React.Component {
    render () {
        return (
            <div>
                高阶函数
            </div>
        )
    }
    componentDidMount () {
        console.log(this)
    }
}
// 暴露这个被hoc增强了的组件
export default hoc(App)

 

 portal

可实现 点击 click 数字加一

 portal1.jsx

import React from "react";
import Modal from "./Modal";
import "./Portalstyle.css"
class Portal1 extends React.Component {
    constructor(props){
        super(props)
        this.handleClick = this.handleClick.bind(this)
    }
    state ={
        count:0
    }
    handleClick = () => {
        // console.log(this.state.count)
        this.setState({
            count: this.state.count+1
        })
    }
    render () {
        return (
            <div className="backgr" onClick={this.handleClick}>
                Portal1{this.state.count}
                <Modal></Modal>
            </div>
        )
    }
}
export default Portal1

Modal.jsx

import React from "react";
import withPortal from "./withPortal";
import './modalstyle.css'
class Modal extends React.Component {
    render () {
        return (
            <div className="portal">
                <button>click</button>
            </div>
        )
    }
}
export default withPortal(Modal)

withPortal.js

import React from "react";
import {createPortal} from 'react-dom'
function withPortal(WrappedComponent) {
    return class  extends React.Component {
        render () {
            return createPortal (
                // 要渲染的内容
                <WrappedComponent {...this.props}/>,
                // 要挂载的地方
                document.querySelector('body')
            )
        }
    }
}
export default withPortal

flux

Flux的流程:
1.组件获取到store中保存的数据挂载在自己的状态上
2.用户产生了操作,调用actions的方法
3.actions接收到了用户的操作,进行一系列的逻辑代码、异步操作
4.然后actions会创建出对应的action,action带有标识性的属性
5.actions调用dispatcher的dispatch方法将action传递给dispatcher
6.dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法
7.store的方法被调用后,更改状态,并触发自己的某一个事件
8.store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的数据

实现一个简易的Flux

点击按钮实现数字加减 

redux.jsx

import React from "react";
import {dispatch} from './store'

class Redux1 extends React.Component {
    render () {
        return (
            <div>
                hello
                <button onClick={dispatch.bind(this,{type:'decrement'})}>-</button>
                <span id="count">count</span>
                <button onClick={dispatch.bind(this,{type:'increment'})}>+</button>
            </div>
        )
    }
    componentDidMount () {
        dispatch()
    }
}
export default Redux1

store.js

// 定义初始状态
const defaultState = {
    count:0
}
// 修改状态
const changeState = (action) => {
    action = action || {type:''}
    switch(action.type) {
        case 'increment' : 
            defaultState.count++
            break
        case 'decrement': 
            defaultState.count--
            break
        default:
    }
}
// DOM 操作
const renderDom = () => {
    let counEle = document.querySelector('#count')
    counEle.innerHTML = defaultState.count
}


const dispatch = (action) => {
    changeState(action)
    renderDom()
}
export {
    dispatch
}


Redux的使用的三大原则:


Single Source of Truth(唯一的数据源)
State is read-only(状态是只读的)
 Changes are made with pure function(数据的改变必须通过纯函数完成)


实现简易的 Redux

点击按钮实现数字加减 

newstore.js

import { changeState } from "./reducer"
let state = {count:0}
const createStore = () => {
   // 获取状态
   const getState = () => {return state}
   // 观察者模式
   const listeners = []
   // 订阅
   const subscribe = (listener) => {listeners.push(listener)}

    const dispatch = (action) => {
        console.log(changeState(state,action))
     state = changeState(state,action)
     // 发布
     listeners.forEach((listener) => {listener()})
    }
    return {
        dispatch,
        getState,
        subscribe
    }
}
const store = createStore()
const render = () => {
    document.querySelector('#count').innerHTML = store.getState().count
}
store.subscribe(render)
export default store

reducer.js


const changeState = (state,action) => {
    action = action || {type:''}
    switch(action.type) {
        case 'increment' : 
            return {
                ...state,
                count:state.count+1
            }
        case 'decrement': 
        return {
            ...state,
            count:state.count-1
        }
        default:
            return state
    }
}
export {
    changeState
}

redux.jsx

import React from "react";
// import {dispatch} from './store'
import store from "./newsore";
class Redux1 extends React.Component {
    render () {
        return (
            <div>
                hello
                <button onClick={store.dispatch.bind(this,{type:'decrement'})}>-</button>
                <span id="count">count</span>
                <button onClick={store.dispatch.bind(this,{type:'increment'})}>+</button>
            </div>
        )
    }
    componentDidMount () {
        store.dispatch()
    }
}
export default Redux1

查看安装的 redux 里面内容

 npm install --save react-redux

重新实现 redux -- todolist

应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要写 reducers

当 state 变化时需要返回全新的对象,而不是修改传入的参数。

把要做的修改变成一个普通对象,这个对象被叫做 action,而不是直接修改 state。然后编写专门的函数来决定每个 action 如何改变应用的 state,这个函数被叫做 reducer

Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器。

todolist.jsx

    import React from "react";
    import Form from "./Form";
    import List from "./List";
    export default class TodoList extends React.Component {
        render () {
            return  (
                <div>
                    <Form></Form>
                    <List></List>
                </div>
            )
        }
    }

 reducer.js

const defaultState = {
    list:[]
}
export default (state = defaultState,action) => {
    switch(action.type) {
        case 'LOAD_DATA':
            return state
        case 'PUT_DATA':
            return {
                list:[
                    ...state.list,
                    action.task
                ]
            }
        case 'REMOVE_DATA':
            // console.log(0)
            let newlist = state.list.filter((value,index) => {
                return index !== action.index
            })
            return {
                ...state,
                list:newlist
            }
        default:
            return state
    }
}

list.jsx

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

const mapStateToProps = (state) => {
    return {
        list:state.list
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        remove(index){
            dispatch({
                type:'REMOVE_DATA',
                index
            })
        }
    }

}
// // 这个方法的返回值是个高阶组件
// // connect(mapDispatchToProps)

class List extends React.Component {
handleClick = (index) =>{
    return () => {
        this.props.remove(index)
    }
}
    render () {
        return (
            <div>
                lsit
                {
                    this.props.list.map((value,index) => {
                        return <li key={index}>{value}
                        <button onClick={this.handleClick(index)}>remove</button>
                        </li>
                    })
                }
            </div>
        )
    }
    componentDidMount(){
        console.log(this)
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(List)

form.jsx

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

const mapDispatchToProps = (dispatch) => {
    return {
        putDate(task) {
            dispatch({
                type:'PUT_DATA',
                task
            })
        }
    }
}
// 这个方法的返回值是个高阶组件
// connect(mapDispatchToProps)
class Form extends React.Component {
    state ={
        task:''
    }
    handleChange = (e) => {
        this.setState({
            task:e.target.value
        })
    }
    handleKeyUp = (e) => {
        if (e.keyCode === 13) {
            console.log(this)
            this.props.putDate(this.state.task)
            this.setState({
                task:''
            })
        }
    }
    render () {
        return (
            <div>
                <input type="text" value={this.state.task} 
                onKeyUp ={this.handleKeyUp}
                onChange={this.handleChange}/>
            </div>
        )
    }
}
export default connect(null,mapDispatchToProps)(Form)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './13redux/03todolist/Todolist';
import reportWebVitals from './reportWebVitals';

import store from './13redux/03todolist/sotre'
ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

redux复杂的异步和同步请求

 redux-thunk 支持 dispatch function,以此让 action creator 控制反转。被 dispatch 的 function 会接收 dispatch 作为参数,并且可以异步调用它。

 安装中间件 :npm install --save redux-thunk ; 

引入  :

import {createStore,applyMiddleware} from 'redux'

import reducer from './reducer'

import thunk from 'redux-thunk'

export default createStore (reducer,applyMiddleware(thunk))

React-route-dom

截止到现在 这个路由版本已经是 6 以上了,因此不少报错的原因就是版本问题。

比如:

在v6版本中,<Switch>被换成了<Routes>

在v6中,component属性被替换成了element,并且需要传带标签的组件而不只是组件名称

等还有更多的变动!!!

小例子

import React from "react";
import { BrowserRouter as Router, Routes, Route} from "react-router-dom";
const Home = () => {
    return (
        <div>
            Home
        </div>
    )
}
const About = () => (<div>About</div>)
const DashBorad = () => (<div>DashBorad</div>)
class App extends React.Component {
    render () {
        return  (
                <Router>
                    <Routes>
                        <Route path='/' element={<Home></Home>}></Route>
                        <Route path='/about' element={<About></About>}></Route>
                        <Route path='/dashborad' element={<DashBorad />}></Route>
                    </Routes>
                </Router>
        )
    }
}
export default App

Immutable Data

不可变数据(Immutable Data)就是一旦创建,就不能再被更改的数据。对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable 对象。

Immutable :

持久化数据结构(Persistent DataStructure),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。

结构共享:(Structural Sharing),为了避免deepCopy把所有节点都复制一遍带来的性能损耗,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值