React學(xué)習(xí)總結(jié)

第1章 課程導(dǎo)學(xué)

學(xué)習(xí)流程

相關(guān)的知識(shí)點(diǎn)

學(xué)習(xí)前提
要有一些js倚评、es6、webpack、npm等基礎(chǔ)知識(shí)

第2章 React初探

2-1 React簡(jiǎn)介

React
FaceBook 推出
2013年開源
函數(shù)式編程
使用人數(shù)最多的前端框架之一
健全的文檔與完善的社區(qū)

2-2 React開發(fā)環(huán)境準(zhǔn)備

1)、確定自己電腦已經(jīng)安裝了node環(huán)境
根據(jù)自己電腦來(lái)安裝屬于自己電腦版本的node,推薦使用穩(wěn)定版
node官網(wǎng)下載地址
2)、安裝react腳手架create-react-app

npx create-react-app my-app
cd my-app
npm start
2-3 工程目錄文件簡(jiǎn)介

因?yàn)橥ㄟ^(guò)腳手架下載的程序苏携,有很多文件是需要我們剔除出去的。
例如沒(méi)用的樣式文件对粪、測(cè)試文件右冻、pwa相關(guān)的一些文件等等

  • 項(xiàng)目結(jié)構(gòu)
  • ├── node_modules # 第三方的依賴庫(kù)文件
  • ├── public
  • │ ├── favicon.ico # Favicon
  • │ ├── index.html # 首頁(yè)
  • ├── src
  • │ ├── app.js # 業(yè)務(wù)代碼文件
  • │ ├── index.js # 應(yīng)用入口
  • ├── README.md
  • └── .gitignore #git忽略提交的文件配置
  • └── package.json #啟動(dòng)、編譯命令設(shè)置著拭;依賴包管理配置信息
  • └── yarn.lock #yarn 依賴包管理配置信息
2-4 React中的組件

下面就是定義了一個(gè)App的組件

//App.js
import React, {Component} from 'react'

class App extends Component {
    render() {
      //JSX 語(yǔ)法
      return (
          <div>hello world!</div>
      )
    }
}
export default App

App組件引用和調(diào)用

//index.js
import React from 'react'
import ReactDOM from 'react-dom'

import App from 'App.js'

ReactDOM.render(<App />,document.getElementById('root'))

JSX語(yǔ)法在使用的時(shí)候纱扭,組件的定義和使用的時(shí)候要大寫
JSX語(yǔ)法中,我們要求一個(gè)組件render函數(shù)返回的內(nèi)容儡遮,外層必須要有一個(gè)大的元素包裹(div或者Fragment)
Fragment渲染到頁(yè)面的時(shí)候是不占位的
eg:

//index.js
import App from 'App.js'  //大寫

ReactDOM.render(<App />,document.getElementById('root'))

第3章 React基礎(chǔ)精講

3-1 使用React編寫TodoList功能
//TodoItem.js
import React, {Component} from 'react';
import PropTypes from 'prop-types'; //組件類型進(jìn)行校驗(yàn)

class TodoItem extends Component {

    constructor(props) {
        super(props);
        this.handleDelete = this.handleDelete.bind(this)
    }

    render() {
        const {content, test} = this.props;

        return (
            <div onClick={this.handleDelete}>{test}-{content}</div>
        )
    }

    handleDelete() {
        const {index, deleteItem} = this.props;
        deleteItem(index)
    }
}

TodoItem.propTypes = {
    test: PropTypes.string.isRequired, //父組件沒(méi)有給子組件傳遞test 但是又是必填項(xiàng)乳蛾,所以可以通過(guò)defaultProps給一個(gè)默認(rèn)值
    content: PropTypes.string,
    deleteItem: PropTypes.func,
    index: PropTypes.number
};

TodoItem.defaultProps = {
    test: 'hello'
};
export default TodoItem


//TodoList.js
import React, {Component, Fragment} from 'react';

import TodoItem from './TodoItem';

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = {
            inputValue: '',
            list: []
        };
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleDelete = this.handleDelete.bind(this);
    }

    render() {
        return (
            <Fragment>
                <div>
                    <label htmlFor="inputArea">輸入內(nèi)容:</label>
                    <input
                        id="inputArea"
                        type="text"
                        value={this.state.inputValue}
                        onChange={this.handleInputChange}
                    />
                    <button onClick={this.handleAdd}>提交</button>
                </div>
                <ul>
                    {this.getTodoItem()}
                </ul>
            </Fragment>
        )
    }

    getTodoItem() {
        return this.state.list.map((item, index) => {
            return (
                <TodoItem
                    content={item}
                    key={index}
                    index={index}
                    deleteItem={this.handleDelete}
                />
            )
        })
    }

    handleInputChange = (e) => {
        const value = e.target.value;
        this.setState(() => {
            return {
                inputValue: value
            }
        })
    };

    handleAdd = () => {
        this.setState((prevState) => {
            return {
                list: [...prevState.list, prevState.inputValue],
                inputValue: ''
            }
        });
    };

    handleDelete = (index) => {
        this.setState((prevState) => {
            const list = [...prevState.list];
            list.splice(index, 1);
            return {list}
        });
    }
}

export default TodoList;

3-2 JSX語(yǔ)法細(xì)節(jié)補(bǔ)充

1.注釋:必須要用花括號(hào)括起來(lái)
單行注釋

{
//這里是單行注釋
}

多行注釋

{/*這是多行注釋*/}

2.樣式名稱引入:將class改為className

因?yàn)閏lass這個(gè)和定義組件“類”有沖突

<input className="inputClass" />

3.表單label for屬性的引用,要將for改為htmlFor
防止for和js中循環(huán)for引起沖突

<label htmlFor="inputArea">用戶名</label>
<input id="inputArea" />

4.將input標(biāo)簽內(nèi)容直接進(jìn)行轉(zhuǎn)義呈現(xiàn)在頁(yè)面 dangerouslySetInnerHTML

 <li
     key={index}
     onClick={this.handleDeleteItem.bind(this, index)}
     dangerouslySetInnerHTML={{__html: item}}
>
</li>
3-3 組件拆分與組件之間的傳值

1.組件拆分
當(dāng)一個(gè)頁(yè)面很大的時(shí)候鄙币,那么我們就會(huì)對(duì)這個(gè)頁(yè)面進(jìn)行組件的拆分肃叶,拆分的結(jié)構(gòu)就像一棵樹一樣


組件之間的結(jié)構(gòu)

2.組件之間的傳值
父組件向子組件傳值是通過(guò)“子組件的屬性”
子組件接受父組件傳過(guò)來(lái)的值:this.props.屬性名稱
子組件向父組件傳值是通過(guò) 子組件調(diào)用父組件的方法,從來(lái)改變父組件的數(shù)據(jù)十嘿,但是要對(duì)這個(gè)方法調(diào)用的時(shí)候因惭,父組件要對(duì)這個(gè)方法進(jìn)行this的綁定

3-4 TodoList 代碼優(yōu)化
//解構(gòu)優(yōu)化
this.props.content ,this.props.index
改為
const {content,index} = this.props
調(diào)用的時(shí)候绩衷,直接使用content蹦魔,index即可

//this的綁定優(yōu)化 都可以寫在constructor里面
onChange={this.handleChange.bind(this)}
constructor(props){
  super(props);
  ....
  ....
  this.handleChange = this.handleChange.bind(this) //優(yōu)化地方
}
調(diào)用的時(shí)候直接寫:
this.handleChange

//Ui優(yōu)化
當(dāng)一段的UI太過(guò)于龐大的是時(shí)候,可以把這段UI放到一個(gè)函數(shù)方法里面咳燕,return 返回出去勿决,同時(shí)在使用的地方調(diào)用一下即可

//setState鍵值對(duì)優(yōu)化為異步函數(shù)
this.setState({
  inputValue: e.target.value
})
改為
this.setState(()=>{
   const value = e.target.value  //在異步里面必須要提出來(lái),如果直接須賦值會(huì)報(bào)錯(cuò)的
   return {
     inputValue:value  //如果將value 改為e.target.value 會(huì)報(bào)錯(cuò)
   }
})

//setState中this.state 改為 參數(shù)prevState
handleAdd = () => {
        this.setState((prevState) => {
            return {
                list: [...prevState.list, prevState.inputValue],
                inputValue: ''
            }
        });
 };

//if else判斷改變?yōu)?switch case也可以提高性能

3-5 圍繞 React 衍生出的思考

聲明式開發(fā)
可以與以其他框架并存
組件化
單項(xiàng)數(shù)據(jù)流(父組件可以向子組件傳值招盲,但是子組件一定不能直接去改變這個(gè)值)
視圖層框架 (負(fù)責(zé)視圖渲染低缩,但是針對(duì)一些大數(shù)據(jù)和復(fù)雜數(shù)據(jù)的時(shí)候,需要交由redux等數(shù)據(jù)框架來(lái)處理)
函數(shù)式編程 (便于自動(dòng)化測(cè)試)

第4章 React高級(jí)內(nèi)容

4-1 React developer tools 安裝及使用

打開chrome瀏覽器曹货,并且打開chrome應(yīng)用商店咆繁,搜索React Developer Tools 添加即可
React Developer Tools 地址
百度網(wǎng)盤下載地址

4-2 PropTypes 與 DefaultProps 的應(yīng)用

創(chuàng)建好的組件,最好給組件定義屬性類型(string控乾、number么介、boolean等等) 和創(chuàng)建默認(rèn)值娜遵。就拿上面todoList這個(gè)例子來(lái)說(shuō)吧蜕衡。
PropTypes
父組件(TodoList)向子組件(TodoItem)傳遞了content(字符串)、index(數(shù)字)、deleteItem(方法)慨仿,那么如果我們?cè)谧咏M件中聲明了這些類型久脯,就可以避免一些不別要的麻煩,如果父組件傳遞過(guò)來(lái)的屬性值不是我們想要的镰吆,那么我就可以告訴瀏覽器我需要什么類型的值
DefaultProps
可以當(dāng)父組件沒(méi)有給子組件傳遞任何值的時(shí)候帘撰,通過(guò)DefaultProps給子組件初始化一些數(shù)據(jù)(即默認(rèn)值)
詳細(xì)代碼見 3-1 使用React編寫TodoList功能
了解更多PropTypes知識(shí)鏈接地址

4-3 props,state 與 render 函數(shù)的關(guān)系

1.當(dāng)組件的state或者props發(fā)生改變時(shí)万皿,render函數(shù)就會(huì)重新執(zhí)行
2.當(dāng)父組件的render函數(shù)被重新執(zhí)行時(shí)摧找,它的子組件的render函數(shù)都將被重新執(zhí)行
eg:
在render函數(shù)的后面進(jìn)行打印
打印父組件console.log('parent-render')
打印子組件console.log('child1-render')
更改父組件的state值
你會(huì)發(fā)現(xiàn)console里面 parent-render 和 child1-render 都被打印出來(lái)了

4-4 React 中的虛擬DOM

1.state 數(shù)據(jù)
2.JSX 模板
3.數(shù)據(jù)+模板 結(jié)合,生成真實(shí)DOM的止,來(lái)顯示
4.state 發(fā)生改變
5.數(shù)據(jù)+模板 結(jié)合线欲,生成真實(shí)DOM欺嗤,替換原始DOM

缺陷:
第一次生成一個(gè)完整的DOM片段
第二次生成一個(gè)完整的DOM片段
第二次的DOM替換第一次的DOM,非常消耗性能

解決综苔??
1.state 數(shù)據(jù)
2.JSX 模板
3.數(shù)據(jù)+模板 結(jié)合位岔,生成真實(shí)DOM如筛,來(lái)顯示
4.state 發(fā)生改變
5.數(shù)據(jù)+模板 結(jié)合,生成真實(shí)DOM抒抬,并不直接替換原始DOM
6.新的DOM(documentFragment)和原始的DOM杨刨,做對(duì)比,找差異
7.找出input框發(fā)生改變的部分
8.只用新的DOM中的input元素瞧剖,替換老的DOM中input的元素

缺陷:
性能的提升并不明顯

接著解決:
1.state 數(shù)據(jù)
2.JSX 模板
3.生成虛擬DOM(其實(shí)就是一個(gè)js對(duì)象拭嫁,用它來(lái)描述真實(shí)DOM)(損耗了性能但是極小)

['div',{id:'abc'},['span',{},'hello world']]

4.用虛擬DOM的結(jié)構(gòu)生成真實(shí)DOM抓于,來(lái)顯示

<div id="abc"><span>hello world</span></div>

5.state 發(fā)生改變
6.數(shù)據(jù)+模板 結(jié)合做粤,生成新的虛擬DOM (極大的提升了性能)

['div',{id:'abc'},['span',{},'bye bye']]

7.比較原始虛擬DOM和生成新的虛擬DOM,找到區(qū)別是span的內(nèi)容(極大的提升了性能)
8.直接操作DOM捉撮,改變span的內(nèi)容

總結(jié)如下:
虛擬DOM其實(shí)就是js對(duì)象怕品,那為什么采用虛擬dom會(huì)提高性能呢?因?yàn)樗麄儽葘?duì)的是js對(duì)象巾遭,而不是真的DOM肉康,從而極大的提升了性能

4-5 深入了解虛擬DOM
JSX(模板) => 虛擬DOM(js對(duì)象) =>真實(shí)的DOM
render() {
  return <div>item</div>
}
等價(jià)于
render() {
  return React.createElement('div',{},'item')
}

虛擬DOM有點(diǎn):
1.性能提升了
2.它是的跨度應(yīng)用得以實(shí)現(xiàn) React Native
因?yàn)閿?shù)據(jù)+模板生成的是虛擬DOM,
1)灼舍、如果是在網(wǎng)頁(yè)中吼和,那么再有虛擬DOM(JS對(duì)象)生成真實(shí)的DOM,瀏覽器可以得以識(shí)別骑素,所以網(wǎng)頁(yè)中可以得到應(yīng)用
2)炫乓、如果在App中,那么再用虛擬DOM(JS對(duì)象)不去生成真實(shí)的DOM,而去生成原生的組件末捣,那么在App中也就可以得到應(yīng)用

4-6 虛擬 DOM 中的 Diff 算法

虛擬DOM什么時(shí)候回發(fā)生比對(duì)呢侠姑?
答:那就是當(dāng)state值發(fā)生改變的時(shí)候,虛擬DOM才會(huì)開始進(jìn)行比對(duì)

為什么setState設(shè)計(jì)成為異步的箩做?
答:我們知道莽红,當(dāng)state發(fā)生改變或者props(即父組件中的state發(fā)生改變)時(shí),也就是當(dāng)我們調(diào)用setState方法的時(shí)候邦邦,state值發(fā)生改變安吁,虛擬DOM開始進(jìn)行比較。
那么如果連續(xù)3次調(diào)用setState方法的時(shí)候燃辖,變更3組數(shù)據(jù)(我們此時(shí)會(huì)想react會(huì)進(jìn)行三次虛擬DOM比對(duì)柳畔,三次渲染頁(yè)面),其實(shí)react會(huì)把短時(shí)間內(nèi)連續(xù)調(diào)用setState方法合并為一個(gè)setState郭赐,只去做一次虛擬DOM的比對(duì)薪韩,然后更新一次DOM,這樣就可以省去額外兩次DOM比對(duì)帶來(lái)的性能消耗捌锭,所以把setState設(shè)計(jì)成為異步的

react中虛擬DOM比對(duì)采用的是diff算法:其實(shí)同層比對(duì)俘陷,key值比對(duì)


同層比對(duì)
key值比對(duì)

所以列表循環(huán)中不要用index作為key值的,在diff算法進(jìn)行比較時(shí)候观谦,會(huì)導(dǎo)致key變更拉盾,而產(chǎn)生一些性能上的問(wèn)題
因?yàn)閕ndex(索引值有時(shí)候會(huì)變更)會(huì)導(dǎo)致key值不穩(wěn)定,
eg:
a 0 b 1 c 2
當(dāng)刪除a
b 0 c 1
使用一個(gè)穩(wěn)定的值作為key才是我們首先要考慮的
eg:
a a b b c c
當(dāng)輸出a
b b c c

4-7 React 中 ref 的使用

ref是幫助我們r(jià)eact直接獲得dom元素豁状,一般情況下我們盡量不要使用ref
eg:

<input
   id="inputArea"
   type="text"
   value={this.state.inputValue}
   onChange={this.handleInputChange}
   ref={(input) => this.input = input}  //ref 使用
/>
 handleInputChange = (e) => {
        //const value = e.target.value;
        const value = this.input.value; //通過(guò)ref 來(lái)獲取input的value值
        this.setState(() => {
            return {
                inputValue: value
            }
        })
};

tip:
當(dāng)我們?cè)谠囉胷ef獲取dom元素的時(shí)候捉偏,有時(shí)候會(huì)出現(xiàn)數(shù)據(jù)不對(duì),或者少一步操作現(xiàn)象泻红,那么因?yàn)閟etState是異步函數(shù)夭禽,解決這個(gè)問(wèn)題就是把ref相關(guān)操作的內(nèi)容放到setState回調(diào)函數(shù)中進(jìn)行操作

<ul ref={(ul)=>this.ul = ul}>
  <div>hello</div>
</ul>

this.setState(() => {
  return {
    inputValue: value
  }
},() => {
  //回調(diào)成功后,在這進(jìn)行ref相關(guān)操作 this.ul.querySelectorAll('div').length
})

在React v16.3 版本中引入的 React.createRef()方法

//使用方法
import React, {Component} from 'react';

class Products extends Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef(); //這個(gè)地方
  }

  handleChangeInputValue = () => {
    console.log(this.myRef.current.value) //取值的時(shí)候借助current屬性
  };

  render() {
    return (
      <div>
        <input type="text" ref={this.myRef} onChange={this.handleChangeInputValue}/>
      </div>
    )
  }
}

export default Products

Ref的適合使用的幾個(gè)場(chǎng)景:

  • 處理焦點(diǎn)谊路、文本選擇或媒體控制讹躯。
this.textInput.current.focus();
  • 觸發(fā)強(qiáng)制動(dòng)畫。
  • 集成第三方 DOM 庫(kù)
4-8 React 的生命周期函數(shù)

生命周期函數(shù):
某一時(shí)刻自動(dòng)調(diào)用執(zhí)行的函數(shù)缠劝。

//雖然不是react的生命周期函數(shù)潮梯,但是有這個(gè)功能,初始化數(shù)據(jù)就放在constructor里面進(jìn)行操作的
1.初始化
constructor(props){
  super(props);
}

2.掛載的生命周期函數(shù)(Mounting)
//當(dāng)組件即將要被掛載到頁(yè)面的時(shí)候惨恭,會(huì)被執(zhí)行
componentWillMount(){}
//render
render(){}
//當(dāng)組件被掛載到頁(yè)面后秉馏,會(huì)被執(zhí)行
componentDidMount(){}

3.數(shù)據(jù)發(fā)生改變更新的生命周期函數(shù)(Updating)
//當(dāng)組件被更新之前,會(huì)被執(zhí)行
shouldComponentUpdate(nextProps,nextState){
  const {list} = this.props;
   if(nextProps.list !== list){
        return true
   }else{
      return false
   }
}

//當(dāng)組件被更新之前脱羡,會(huì)被執(zhí)行萝究,但是在shouldComponentUpdate后面
//如果shouldComponentUpdate 返回true 它才執(zhí)行
//如果返回 false 它不會(huì)被執(zhí)行
componentWillUpdate(){}

//當(dāng)組件被更新之后,會(huì)被執(zhí)行
componentDidUpdate(){}

//一個(gè)組件要從父組件接受參數(shù)
//如果這個(gè)組件第一次存在于父組件中母廷,不會(huì)被執(zhí)行
//如果這個(gè)組件之前已經(jīng)存在父組件中,才會(huì)被執(zhí)行
componentWillReceiveProps(nextProps){}

4.組件從頁(yè)面卸載的生命周期函數(shù)
//當(dāng)這個(gè)組件將要被從頁(yè)面剔除出去的時(shí)候糊肤,會(huì)被執(zhí)行
componentWillUnmount(){}

react生命周期函數(shù)
4-9 React 生命周期函數(shù)的使用場(chǎng)景

1.生命周期使用場(chǎng)景

//針對(duì)組件的優(yōu)化的時(shí)候,可以使用shouldComponentUpdate氓鄙,當(dāng)組件當(dāng)中的屬性值發(fā)生改變的是去做更新馆揉,沒(méi)有改變則不去更新,減少組件被重復(fù)多次渲染的頻率抖拦,減少性能的消耗
shouldComponentUpdate(nextProps, nextState) {
        const {content} = this.props;
        if (nextProps.content !== content) {
            return true
        } else {
            return false
        }
    }

//ajax接口請(qǐng)求 放在componentDidMount()里面
import axios from 'axios'
componentDidMount(){
  axios.get('api')
     .then((res)=>{
        console.log('success')
      })
    .catch(()=>{
        console.log('error')
    })
}

2.性能優(yōu)化
1)升酣、shouldComponentUpdate
2)、setState采用的是異步回調(diào)的方式态罪,提高了性能
3)噩茄、虛擬DOM中的同層比對(duì)和key值比對(duì),也極大的提高了性能

4-10 使用Charles實(shí)現(xiàn)本地?cái)?shù)據(jù)mock

charles 下載安裝

//桌面創(chuàng)建一個(gè)todoList.json文件
["react","vue","angular"]

//設(shè)置charles
打開 charles ---> 菜單欄tools ---> Map Local Setting

//請(qǐng)求接口
componentDidMount() {
        axios.get('/api/todoList')
            .then((res) => {
                this.setState(() => {
                    return {
                        list: [...res.data]
                    }
                })
            })
            .catch(() => {
                console.log('error')
            })
}
Map Local Setting設(shè)置
4-11 React 中實(shí)現(xiàn) CSS 過(guò)渡動(dòng)畫

css過(guò)度動(dòng)畫主要還是css3知識(shí)點(diǎn)的理解和認(rèn)識(shí)复颈,比如:transition(all 1s ease-in)绩聘、animation(show-item 1s ease-in forwards)、@keyframes等等
還就就是借助第三方庫(kù)react-transition-group等等

//state
 constructor(props) {
        super(props);
        this.state = {
            show: true
        };

        this.handleToggle = this.handleToggle.bind(this);
}

//JSX
<h3>動(dòng)畫部分</h3>
<div className={this.state.show ? 'show' : 'hide'}>hello</div>
<button onClick={this.handleToggle}>toggle</button>

//綁定事件
handleToggle() {
        this.setState((prevState) => {
            return {
                show: prevState.show === true ? false : true
            }
        })
}

//style
.show {
    /*opacity: 1;*/
    /*transition: all 1s ease-in;*/

    animation: show-item 2s ease-in forwards;
}

.hide {
    /*opacity: 0;*/
    /*transition: all 1s ease-in;*/
    animation: hide-item 2s ease-in forwards;
}

@keyframes show-item {
    0% {
        opacity: 0;
        color: red;
    }
    50% {
        opacity: 0.5;
        color: green;
    }
    100% {
        opacity: 1;
        color: blue;
    }
}

@keyframes hide-item {
    0% {
        opacity: 1;
        color: red;
    }
    50% {
        opacity: 0.5;
        color: green;
    }
    100% {
        opacity: 0;
        color: blue;
    }
}
4-12 使用 react-transition-group 實(shí)現(xiàn)動(dòng)畫
import  { CSSTransition } from 'react-transition-group';
<CSSTransition
   in={this.state.show}
   timeout={1000}
   classNames="fade"
   unmountOnExit
   onEntered={(el)=>{el.style.color='blue'}}
   appear={true}
>
   <div>hello</div>
</CSSTransition>

//css

/*CSSTransition*/
.fade-enter,fade-appear{
    opacity: 0;
}
.fade-enter-active,fade-appear-active{
    opacity: 1;
    transition: opacity 1s ease-in;
}
.fade-enter-done{
    opacity: 1;
}

.fade-exit{
    opacity: 1;
}
.fade-exit-active{
    opacity: 1;
    transition: opacity 1s ease-in;
}
.fade-exit-done{
    opacity: 0;
}

當(dāng)我們想一個(gè)數(shù)組都擁有動(dòng)畫的時(shí)候耗啦,我們會(huì)用react-transition-group里面的TransitionGroup
了解更多鏈接地址

5-1 Redux 入門

-1 Redux 概念簡(jiǎn)述

react是一個(gè)視圖層框架
那么凿菩,redux就是一個(gè)數(shù)據(jù)層框架
Redux = Reducer + Flux


image.png
5-2 Redux 的工作流程
Redux 的工作流程

React Component:借書者
Action Creators: 我要借本三國(guó)演義這句話
Store: 圖書館管理員
Reducer:記錄本

React Component:我想要借本三國(guó)演義
Action Creators:那我給你寫封信(dispatch 發(fā)號(hào)了一個(gè)action指令),我把這個(gè)指令告訴了Store
Store:我知道了帜讲,但是我不知道這本書放到哪了衅谷,我?guī)湍銌?wèn)問(wèn)Reducers
Reducer:我給你查查,查到了似将,在這呢获黔。帶著三國(guó)演義這本書(newState)給了Store,Store記錄下了借書的內(nèi)容信息在验,并這本書最終給了React Components

5-3 使用 Antd 實(shí)現(xiàn) TodoList 頁(yè)面布局
//1.安裝antd并且引入antd樣式文件
npm install antd -S

//2.todoList.js
import React, {Component} from 'react';
import {Input, Button, List} from 'antd';

import 'antd/dist/antd.css';

import store from './store'

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.state = store.getState();
    }

    render() {
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.state.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                />
                <Button type="primary" >提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => (<List.Item>{item}</List.Item>)}
                ></List>
            </div>
        )
    }

    handleInputValue(e) {
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
        store.dispatch(action)
    }

}

export default TodoList;
5-4 創(chuàng)建 redux 中的 store
//在src目錄下創(chuàng)建store目錄玷氏,并在store目錄下創(chuàng)建index.js(圖書館管理員)和reducer.js(記錄本)

//index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store

//reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
};

export default (state = defaultState, action) => {
    return state
}

//TodoList.js
import store from './store'
constructor(props) {
     super(props);
     this.state = store.getState();
     console.log(store.getState()) //打印出來(lái)的數(shù)據(jù)是{inputValue: "123",Array(2)}
}

創(chuàng)建圖書館store 并打通 store和reducer之間的橋梁,
然后react component 通過(guò) store.getState()方法拿到 reducer里面的數(shù)據(jù)了

5-5 Action 和 Reducer 的編寫

1.Redux DevTools安裝和配置
去chrome應(yīng)用商店 安裝 Redux DevTools這個(gè)瀏覽器插件
Redux DevTools下載鏈接地址
安裝完了以后腋舌,發(fā)現(xiàn)用chrome瀏覽器打開控制臺(tái)预茄,點(diǎn)擊redux標(biāo)簽,并沒(méi)有顯示任何內(nèi)容侦厚,那是因?yàn)樾枰覀冊(cè)趕tore文件中寫一段代碼耻陕。
配置相關(guān)信息的地址,打開github刨沦,搜索 redux-devtools-extension诗宣,就能查看相關(guān)的配置信息了

import {createStore} from 'redux';
import reducer from './reducer';

const store = createStore(
    reducer, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() //這段代碼,為了方便瀏覽器能捕獲到redux數(shù)據(jù)
);

export default store

2.redux的工作流程原理演示(todoList這個(gè)例子來(lái)說(shuō))
根據(jù)上面的redux工作流想诅,咱們來(lái)說(shuō)召庞。
如果我們想要更改React Component(input里面的值)
那么我們需要?jiǎng)?chuàng)建一個(gè)Action creators,并且定義一個(gè)action對(duì)象岛心,通過(guò)store.dispatch方法傳遞給store

handleInputValue(e) {
       //定義一個(gè)action
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
      // 通過(guò)store.dispatch方法,把a(bǔ)ction傳遞給store
        store.dispatch(action)
}

當(dāng)store接受到Action creators發(fā)送過(guò)來(lái)的action的時(shí)候篮灼,它說(shuō)我需要我的秘書reducer幫我查詢一下忘古,并且?guī)臀姨幚硪幌隆?br> 于是乎reducer就就收了兩個(gè)參數(shù),第一個(gè)是state(定義初始化的舊數(shù)據(jù))诅诱,第二個(gè)就是傳遞過(guò)來(lái)的action(一個(gè)type名稱髓堪,一個(gè)是傳遞過(guò)來(lái)新的inputValue值),
reducer說(shuō):我不能直接去更改state里面的值,我需要把state值通過(guò)JSON的parse和stringify進(jìn)行數(shù)據(jù)深層拷貝生成newState娘荡。那么在對(duì)這個(gè)newState進(jìn)行數(shù)據(jù)的處理干旁,最后把處理好的數(shù)據(jù)再return 回去
store拿到 reducer處理好的新數(shù)據(jù)后,
再通過(guò)自己的store.getState()方法炮沐,去拿到reducer的最新數(shù)據(jù)
再通過(guò)自己的store.subscribe()方法争群,去監(jiān)測(cè)store里面的數(shù)據(jù)變化,
最后通過(guò)this.setState()方法大年,把最新的數(shù)據(jù)渲染到頁(yè)面上去

通過(guò)第一方法專門是獲得最新數(shù)據(jù)的store.subscribe(this.handleStore);

//reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
};

export default (state = defaultState, action) => {
    console.log(state, action);
    if (action.type === 'change_input_value') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState
    }

    if (action.type === 'add_list_item') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState
    }
    return state
}

//TodoList.js
import React, {Component} from 'react';
import {Input, Button, List} from 'antd';

import 'antd/dist/antd.css';

import store from './store'

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleStore = this.handleStore.bind(this);
        this.state = store.getState(); 

        store.subscribe(this.handleStore);//檢測(cè)handleStore方面里面數(shù)據(jù)的變化
    }

    render() {
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.state.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={this.handleInputValue}
                />
                <Button type="primary" onClick={this.handleAdd}>提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => (<List.Item>{item}</List.Item>)}
                ></List>
            </div>
        )
    }

    handleInputValue(e) {
        //定義一個(gè)action 把這個(gè)type:干什么事情的名字傳遞過(guò)去换薄,并且發(fā)生更改的信息傳遞過(guò)去
        const action = {
            type: 'change_input_value',
            value: e.target.value
        };
        store.dispatch(action)
    }

    handleAdd() {
        const action = {
            type: 'add_list_item'
        };
        store.dispatch(action)
    }

    handleStore() {
        //把變更后新的數(shù)據(jù),重新放入到state中翔试,然后去渲染頁(yè)面
        this.setState(store.getState());
    }

}

export default TodoList;
5-6 ActionTypes 的拆分

為什么要把a(bǔ)ction里面的type 拆分到一個(gè)文件里面呢专控?
第一當(dāng)我們把type值拼寫錯(cuò)誤的時(shí)候,不好找錯(cuò)
第二我們需要調(diào)用相同的內(nèi)容遏餐,寫兩次
所以我們?cè)趕tore文件下面創(chuàng)建了一個(gè)actionType.js

5-7 使用 actionCreator 統(tǒng)一創(chuàng)建 action

為什么要把把組件中的action通過(guò)方法的形式拆分出去呢伦腐?
第一為了方便action的統(tǒng)一管理
第二為了減少React Component 代碼的繁瑣

//在store文件下創(chuàng)建actionCreators.js
import {CHANGE_INPUT_VALUE, ADD_LIST_ITEM, DELETE_LIST_ITEM} from './actionTypes';

export const getChangeInputValue = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getAddListItem = () => ({
    type: ADD_LIST_ITEM
});

export const getDeleteListItem = (index) => ({
    type: DELETE_LIST_ITEM,
    index
});

//TodoList.js
import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';
handleInputValue(e) {
        const action = getChangeInputValue(e.target.value);
        store.dispatch(action)
}

handleAdd() {
        const action = getAddListItem();
        store.dispatch(action)
}

handleDeleteItem(index) {
        const action = getDeleteListItem(index);
        store.dispatch(action);
}
5-8 Redux 知識(shí)點(diǎn)復(fù)習(xí)補(bǔ)充

1.Redux在設(shè)計(jì)和使用的三個(gè)原則
1).store是唯一的
2).只有store能改變自己的內(nèi)容
3).reducer必須是個(gè)純函數(shù)(給個(gè)固定的輸入,就一定會(huì)有固定的輸出失都,且不會(huì)有任何副作用)
所以里面不能有異步操作(ajax)柏蘑,不能有時(shí)間的操作new Date()
2.redux中核心的API
1).createStore 幫助我們創(chuàng)建一個(gè)store
2).store.dispatch() 幫助我們派發(fā)一個(gè)action
3).store.getState() 幫助我們獲得store當(dāng)中所有的數(shù)據(jù)
1).store.subscribe() 幫助我們訂閱(監(jiān)測(cè))store當(dāng)中數(shù)據(jù)的改變

第6章 Redux進(jìn)階

6-1 UI組件和容器組件

1.UI組件
UI組件負(fù)責(zé)頁(yè)面的渲染
eg:

import React, {Component} from 'react'
import {Button, Input,List} from "antd";

class TodoListUi extends Component{
    render(){
        return (
            <div style={{marginTop: '10px', marginLeft: '10px'}}>
                <Input
                    value={this.props.inputValue}
                    placeholder="todo info"
                    style={{width: '300px', marginRight: '10px'}}
                    onChange={this.props.handleInputValue}
                />
                <Button type="primary" onClick={this.props.handleAdd}>提交</Button>
                <List
                    style={{marginTop: '10px', width: '300px'}}
                    bordered
                    dataSource={this.props.list}
                    renderItem={(item, index) => (
                        <List.Item onClick={() => this.props.handleDeleteItem(index)}>{item}</List.Item>)}
                ></List>
            </div>
        )
    }
}

export default  TodoListUi;

2.容器組件
它不管你的UI長(zhǎng)成什么樣子,它只負(fù)責(zé)頁(yè)面的業(yè)務(wù)邏輯

import React, {Component} from 'react';
import TodoListUi from './TodoListUi';

import 'antd/dist/antd.css';

import store from './store'
import {getChangeInputValue, getAddListItem, getDeleteListItem} from './store/actionCreators';

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.handleInputValue = this.handleInputValue.bind(this);
        this.handleAdd = this.handleAdd.bind(this);
        this.handleDeleteItem = this.handleDeleteItem.bind(this);
        this.handleStore = this.handleStore.bind(this);
        this.state = store.getState();

        store.subscribe(this.handleStore);
    }

    render() {
        return (
            <TodoListUi
                inputValue={this.state.inputValue}
                list={this.state.list}
                handleInputValue={this.handleInputValue}
                handleAdd={this.handleAdd}
                handleDeleteItem={this.handleDeleteItem}
            />
        )
    }

    handleInputValue(e) {
        const action = getChangeInputValue(e.target.value);
        store.dispatch(action)
    }

    handleAdd() {
        const action = getAddListItem();
        store.dispatch(action)
    }

    handleDeleteItem(index) {
        const action = getDeleteListItem(index);
        store.dispatch(action);
    }

    handleStore() {
        this.setState(store.getState());
    }

}

export default TodoList;
6-2 無(wú)狀態(tài)組件

當(dāng)一個(gè)組件只有render函數(shù)的時(shí)候粹庞,那么就可以把這個(gè)組件改寫為無(wú)狀態(tài)組件
優(yōu)勢(shì):性能比較高
無(wú)狀態(tài)組件是一個(gè)函數(shù)
而一般組件是聲明的一個(gè)類咳焚,這個(gè)類里面還有一些生命周期函數(shù),所以執(zhí)行起來(lái)不僅需=要執(zhí)行類還要執(zhí)行render
那么什么時(shí)候去用無(wú)狀態(tài)組件呢庞溜?
當(dāng)我們定義UI組件的時(shí)候革半,因?yàn)闆](méi)有業(yè)務(wù)邏輯,只有一個(gè)render流码,所以一般在情況下又官,在UI組件中我們使用無(wú)狀態(tài)組件比較多一些

//無(wú)狀態(tài)組件
import React from 'react'
import {Button, Input, List} from "antd";

const TodoListUi = (props) => {
    return (
        <div style={{marginTop: '10px', marginLeft: '10px'}}>
            <Input
                value={props.inputValue}
                placeholder="todo info"
                style={{width: '300px', marginRight: '10px'}}
                onChange={props.handleInputValue}
            />
            <Button type="primary" onClick={props.handleAdd}>提交</Button>
            <List
                style={{marginTop: '10px', width: '300px'}}
                bordered
                dataSource={props.list}
                renderItem={(item, index) => (
                    <List.Item onClick={() => props.handleDeleteItem(index)}>{item}</List.Item>)}
            ></List>
        </div>
    )
};

export default TodoListUi;
6-3 Redux 中發(fā)送異步請(qǐng)求獲取數(shù)據(jù)
//在actionTypes.js中創(chuàng)建一個(gè)變量
export const INIT_LIST_DATA = 'init_list_data';

//在actionCreators.js中創(chuàng)建一個(gè)action
export const getTodoListData = (data) => ({
    type: INIT_LIST_DATA,
    data
});

/TodoList.js
import axios from 'axios';
import {getTodoListData} from './store/actionCreators';
componentDidMount() {
        axios.get('/api/todoList')
            .then((res) => {
                const data = res.data;
                const action = getTodoListData(data);
                store.dispatch(action)
            })
}

//reducer.js
import {INIT_LIST_DATA} from './actionTypes.js'
const defaultState = {
  inputValue:'',
  list:[]
}
export default (state = defaultState, action) =>{
     if(action.type === INIT_LIST_DATA){
      const newState = JSON.parse(JSON.stringify(state))
      newState.list = action.data
      return newState
    }
    return state
}
6-4 使用Redux-thunk 中間件實(shí)現(xiàn)ajax數(shù)據(jù)請(qǐng)求

1.Redux-thunk 安裝 以及 redux-Devtools的配置

//安裝
npm install redux-thunk -S

//redux-Devtools的配置 store文件下的index.js
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(thunk),
);

const store = createStore(reducer, enhancer);

export default store

配置參考地址鏈接:redux-devtools-extension

2.redux-thunk 在程序中的應(yīng)用
為什么使用redux-thunk這個(gè)redux中間件?
第一:可以把數(shù)據(jù)操作和數(shù)據(jù)請(qǐng)求操作從React Component中搬到ActionCreators.js里面漫试,不會(huì)是的組件顯得那么擁堵
第二:便于后期的單元測(cè)試

//actionCreators.js中的修改 
import axios from 'axios';
export const getTodoListDataAction = (data) => ({
    type: INIT_LIST_DATA,
    data
});

export const getListData = () => {
    //redux-thunk 返回action是一個(gè)函數(shù)的時(shí)候六敬,且放到了action里面進(jìn)行操作的
    return (dispatch) => {
        axios.get('/api/todoList')
            .then((res) => {
                const data = res.data;
                const action = getTodoListDataAction(data);
                dispatch(action)
            })
    }
};

//TodoList.js中的修改
import { getListData } from './store/actionCreators';
componentDidMount() {
        const action = getListData();
        store.dispatch(action);
}
6-5 什么是Redux的中間件
redux數(shù)據(jù)工作流

redux-thunk 其實(shí)是對(duì)store.dispatch(action)方法的一個(gè)封裝和升級(jí),是把異步請(qǐng)求的操作放到了action當(dāng)中進(jìn)行操作驾荣。
在沒(méi)有使用redux-thunk的時(shí)候外构,定義的action是一個(gè)對(duì)象
使用redux-thunk之后普泡,定義的action不僅可以是對(duì)象,而且還可以可以是一個(gè)函數(shù)

其他redux中間件:

redux-logger:可以記錄action每次派發(fā)的日志
redux-saga:也是解決react中異步的一個(gè)中間件审编,單獨(dú)的把異步的操作放到一個(gè)文件中進(jìn)行操作

6-8 Redux-saga中間件入門

1.redux-sage的安裝和配置

//安裝
npm install redux-saga -S

//配置是在 store文件下面的index.js中
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
// import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import todoSaga from './sagas'

const sagaMiddleware = createSagaMiddleware();



const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(sagaMiddleware),
);

const store = createStore(reducer, enhancer);
sagaMiddleware.run(todoSaga);

export default store

//sagas.js

配置參考地址鏈接:redux-saga

2.redux-saga在程序中的應(yīng)用

//TodoList.js
componentDidMount() {
        const action = getTodoListData();
        store.dispatch(action);
}

//actionTypes.js
export const INIT_LIST_DATA = 'init_list_data';

//actionCreators.js
import {INIT_LIST_DATA} from './actionTypes';
export const getTodoListData = (data) => ({
    type: INIT_LIST_DATA,
    data
});

//reducer.js
import {INIT_LIST_DATA} from './actionTypes';
const defaultState = {
    inputValue: '',
    list: []
};
export default (state = defaultState, action) => {
    // console.log(state, action);
    if (action.type === INIT_LIST_DATA) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list = action.data;
        return newState
    }
    return state
}

//sagas.js
import {takeEvery, put} from 'redux-saga/effects';
import {INIT_LIST_DATA} from './actionTypes';
import {getTodoListData} from './actionCreators';
import axios from 'axios';

function* getListData() {
    try {
        const res = yield axios.get('/api/todoList');
        const action = getTodoListData(res.data);
        yield put(action)
    } catch(e) {
        console.log('todoList 網(wǎng)絡(luò)異常')
    }
}

function* todoSaga() {
    yield takeEvery(INIT_LIST_DATA, getListData);
}

export default todoSaga;

總結(jié):
1).ajax請(qǐng)求
不采用Promise那種形式了(.then)撼班,而是通過(guò)yield 來(lái)等待返回的結(jié)果
2).接受或者監(jiān)聽Action的
通過(guò)的takeEvery,檢測(cè)到變量名稱垒酬,觸發(fā)一個(gè)generator函數(shù)
3).派發(fā)請(qǐng)求
不采用store.dispatch(), 而是通過(guò)的是put()
4).出了takeEvery砰嘁、put方法還有(takeLatest、call等等多種API)

6-9 如何使用 React-redux

react-redux核心API有哪些伤溉?
1).Provider:就是一個(gè)連接器的組件,因?yàn)镻rovider和store做了關(guān)聯(lián)妻率,所以Provider這些內(nèi)部的組件都可以獲取到store里面的數(shù)據(jù)內(nèi)容了

//安裝react-redux
npm install react-redux -S

//使用 在src文件下面的index.js文件進(jìn)行編寫
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider }  from 'react-redux'
import TodoList from './TodoList'
import store from './store'

const App = (
      <Provider store={store}>
        <TodoList />
      </Provider>
)

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

2).connect: 是React Component調(diào)用react-redux的connect方法乱顾,使得組件和store關(guān)聯(lián)起來(lái),并且能對(duì)state進(jìn)行設(shè)置和修改

import React,{ Component } from 'react'
import {connect} from 'react-redux;

class TodoList extends Component {
    render() {
      return (
          <div>
                 <div>
                    <input 
                        value={this.props.inputValue} 
                        onChange={this.props.handleInputChange}
                    />
                    <button>提交</button>
                </div>
                <div>
                    <ul><li>hello</li></ul>
                </div>
          </div>
      )
    }
}
const mapStateToProps = (state) => {
    return {
        inputValue: state.inputValue
    }
}

//store.dispatch,props
mapDispatchToProps = (dispatch) => {
      return {
          handleInputChange(e) {
              const action = {
                  type:'change_input_value',
                  value: e.target.value
               }
              dispatch(action)
          }
      }
}

export default connect(mapStateToProps,mapDispatchToProps)(TodoList)

6-12 最終TodoList功能
通過(guò)react官網(wǎng)提供的腳手架工具(creat-react-app)來(lái)搭建項(xiàng)目
1).采用了react全家桶:
react
react-dom
react-redux
redux
redux-thunk
2).ajax請(qǐng)求
axios
3).項(xiàng)目目錄


項(xiàng)目目錄

4).代碼展示

//src文件下的 index.js
import React from 'react'
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import TodoList from './TodoList'
import store from './store'

const App = (
    <Provider store={store}>
        <TodoList/>
    </Provider>
)

ReactDOM.render(App, document.getElementById('root'));

//TodoList.js
import React, {Component} from 'react'
import {connect} from "react-redux";
import {getInputValueAction, getHandleClickAction, getDeleteItemAction, getListDataApi} from './store/actionCreators'

class TodoList extends Component {
    render() {
        const {inputValue, list, handleInputChange, handleClick, handleDelete} = this.props;
        return (
            <div>
                <div>
                    <input
                        type="text"
                        value={inputValue}
                        onChange={handleInputChange}
                    />
                    <button onClick={handleClick}>提交</button>
                </div>
                <div>
                    <ul>
                        {
                            list.map((item, index) => (
                                <li key={index} onClick={() => handleDelete(index)}>{item}</li>
                            ))
                        }
                    </ul>
                </div>
            </div>
        )
    }

    componentDidMount() {
        this.props.getListData()
    }
}

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

const mapDispatchToProps = (dispatch) => {
    return {
        handleInputChange(e) {
            const action = getInputValueAction(e.target.value);
            dispatch(action)
        },
        handleClick() {
            const action = getHandleClickAction();
            dispatch(action)
        },
        handleDelete(index) {
            const action = getDeleteItemAction(index);
            dispatch(action)
        },
        getListData() {
            const action = getListDataApi();
            dispatch(action);
        }
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

//store 文件下的index.js
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);

export default store;

//store 文件下的reducer.js
import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';

const defaultState = {
    inputValue: '',
    list: []
};
export default (state = defaultState, action) => {
    if (action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.inputValue = action.value;
        return newState
    }
    if (action.type === ADD_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        return newState
    }
    if (action.type === DELETE_ITEM) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index, 1);
        return newState
    }
    if (action.type === GET_LIST_DATA) {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list = action.data;
        return newState
    }
    return state
}

//store 文件下的actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_ITEM = 'add_item';
export const DELETE_ITEM = 'delete_item';
export const GET_LIST_DATA = 'get_list_data';

//store 文件下的actionCreators.js
import axios from 'axios';
import {CHANGE_INPUT_VALUE, ADD_ITEM, DELETE_ITEM, GET_LIST_DATA} from './actionTypes';

export const getInputValueAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
});

export const getHandleClickAction = () => ({
    type: ADD_ITEM
});

export const getDeleteItemAction = (index) => ({
    type: DELETE_ITEM,
    index
});

export const getListDataAction = (data) => ({
    type: GET_LIST_DATA,
    data
});

export const getListDataApi = () => {
    return (dispatch) => {
        axios.get('/api/todoList')
            .then(res => {
                const data = res.data;
                const action = getListDataAction(data);
                dispatch(action)
            })
            .catch((e) => {
                console.log('/api/todoList 網(wǎng)絡(luò)異常')
            })
    }
};

第7章 項(xiàng)目實(shí)戰(zhàn)中的一些技巧

7-1 styled-components的應(yīng)用

在寫react組件的時(shí)候宫静,為了防止樣式被污染到走净,我們可以通過(guò)styled-components自定義標(biāo)簽以及樣式。

//1.安裝 styled-components
npm install styled-components -S

//2.初步使用方法孤里,創(chuàng)建一個(gè)style.js文件
import styled from 'styled-components';

export const Nav = styled.div`
  width:1000px;
  margin: 0 auto;
  height: 50px;
  line-height: 50px;
  &.txtColor{
    color:red
  }
`
組件中引用
import {Nav} from './style.js'
<Nav className="txtColor">

//3.attrs屬性
export const NavItem = styled.a.attrs({
  href: '/'
})`
  //樣式
`
export const NavItem = styled.input.attrs({
  placeholder: '搜索'
})`
  //樣式
`

//4.嵌套的應(yīng)用
import { Nav,NavItem} from './style.js'
<Nav>
  <NavItem className="bg">首頁(yè)</NavItem>
</Nav>
export const Nav = styled.div`
  width:1000px;
  margin: 0 auto;
  height: 50px;
  line-height: 50px;
  &.txtColor{
    color:red
  }
  //嵌套寫法
  .bg{
      background: red
  }
`

//5.全局樣式的使用(createGlobalStyle)伏伯,比如reset.css、iconfont.css等等
export const GlobalStyle = createGlobalStyle`
//reset.css內(nèi)容 或者 iconfont.css 內(nèi)容等等
`;
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import Main from './Main';
import store from './store';
import {GlobalStyle} from './style';
import {GlobalStyle2} from './statics/font/iconfont'

const App = (
    <Provider store={store}>
        <GlobalStyle/> //這個(gè)地方就可以設(shè)置為全局樣式了
        <GlobalStyle2/>//這個(gè)地方就可以設(shè)置為全局樣式了
        <Main/>
    </Provider>
);

ReactDOM.render(App, document.getElementById('root'));

//6.參數(shù)傳遞和獲取
應(yīng)用場(chǎng)景捌袜,當(dāng)我們都在循環(huán)一個(gè)列表的數(shù)據(jù)的時(shí)候说搅,需要傳遞這個(gè)img作為它的背景圖片
<Toppic imgUrl = '......png'></Topic>
<Toppic imgUrl = '......png'></Topic>

import styled from 'styled-components'
export const Topic = styled.div`
  background:url(${(props)=>props.imgUrl})
`;

上面是styled-components的一些常用的使用方法,要是想學(xué)習(xí)和了解更多虏等。
styled-components更多學(xué)習(xí)和了解地址

7-2 redux中 combinReducers 的使用

在開發(fā)過(guò)程中弄唧,我們不可能把所有的reducer放到一個(gè)文件里面,那么肯定需要對(duì)reducer進(jìn)行拆分的霍衫,但是拆分后的reducer最后我們肯定需要在合并到一起呢候引,因?yàn)樵趓edux在創(chuàng)建store的時(shí)候,需要reducer的集合作為入?yún)⒌亩氐K泻喜educer就誕生了combinReducers

import { combinReducers } from 'reducer'
import {reducer as headerReducer} from '../common/header/store'
import {reducer as footerReducer} from '../common/footer/store'
...

const reducer = combinReducers({
  header: headerReducer,
  footer: footerReducer
  ...
})
export default reducer

ps: 調(diào)用的時(shí)候注意了

const mapState = (state) => {
    return {
        focused: state.header.focused //調(diào)用時(shí)候澄干,你加上你header或者footer
    }
};
7-3 store的拆分
store的拆分

上面我們寫TodoList的demo的是,因?yàn)橹簧婕皟蓚€(gè)頁(yè)面柠傍,所以不會(huì)考慮到store的拆分麸俘,但是在我們制作項(xiàng)目的時(shí)候,我們就的考慮store的拆分了

1.最外層的store文件(store總中心):(index.js 和 reducer.js)
index.js:創(chuàng)建store惧笛,并且把store和reducer鏈接起來(lái)疾掰,而且配置了redux-devtools可以讓我們?cè)赾hrome里面看到redux的變化
reducer.js: 把項(xiàng)目中各個(gè)地方的reducer通過(guò)combinReducers方便合并起來(lái),把合并的最終結(jié)果reducer徐紧,暴露出去

//index.js
import {createStore, applyMiddleware, compose} from "redux";
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
    applyMiddleware(thunk),
);
const store = createStore(reducer, enhancer);

export default store;

//reducer.js
import {combineReducers} from "redux";
import {reducer as headerReducer} from '../common/header/store'

const reducer = combineReducers({
    header: headerReducer
});

export default reducer;

2.組件中的store文件(碎片store,那公共header為例)的拆分:
index.js: 把下面三個(gè)文件整合到一起静檬,并且暴露出去炭懊,為了更方便的引用
constants.js: 定義了一些大寫的常量,且暴露出去這些常量
actionCreators.js: header組件中action的一些方法都放在這個(gè)里面拂檩,且暴露出去
reducer.js: header組件中的reducer操作放在這個(gè)里面侮腹,且暴露出去

//index.js:
import reducer from './reducer';
import * as constants from './constants';
import * as actionCreators from './actionCreators';
export {
    reducer, //為了總store中reducer更方便的引入
    constants,//為了actionCreator更方便的引入
    actionCreators//為了組件中更方便的引入
}
//總store中reducer引入的時(shí)候:import {reducer as headerReducer} from '../common/header/store'
//actionCreator引入的時(shí)候:import {constants} from './index';
//header組件引入的時(shí)候:import {actionCreators} from './store';

//constants.js
export const INPUT_FOCUSED = 'header/input_focused';
export const INPUT_BLUR = 'header/input_blur';

//actionCreators.js
import {constants} from './index';
export const getInputFocusedAction = () => ({
    type: constants.INPUT_FOCUSED
});
export const getInputBlurAction = () => ({
    type: constants.INPUT_BLUR
});
7-4 使用 Immutable.js 來(lái)管理store中的數(shù)據(jù)

為了確保原始state不會(huì)被修改,導(dǎo)致的一些問(wèn)題稻励。所以我們引入了Immutable.js來(lái)更好維護(hù)我們的原始state數(shù)據(jù)

//1.安裝 immutable 
npm install immutable -S

//2.使用 immutable中的fromJS 可以把 js對(duì)象轉(zhuǎn)變?yōu)?immutable對(duì)象
import {constants} from './index';
import { fromJS } from 'immutable';
const defaultState = fromJS({
  focused:false
})

//3.設(shè)置  更改state里面的 immutable數(shù)據(jù)那么就需要.set()方法
//immutable對(duì)象的set方法父阻,會(huì)結(jié)合之前immutable對(duì)象的值和設(shè)置的值,返回一個(gè)全新的對(duì)象
export default (state = defaultState,action) =>{
    if(action.type === constants.INPUT_FOCUSED)  {
        return state.set('focused',true)
    }
   if(action.type === constants.GET_HEADER_LIST)  {
        //return state.set('list', ).set('totalPage', );
        //改為 state.merge()方法的
         return state.merge({
                list: action.data,
                totalPage:action.totalPage
         });
    }
    return state
}

//4.獲取   要使用immutable數(shù)據(jù) 要通過(guò).get方法
const mapState = (state) =>{
  return {
    focused: state.header.get('focused')
  }
}

//5.獲取  當(dāng)需要把immutable對(duì)象轉(zhuǎn)化為 普通的js對(duì)象時(shí)候
const {list} = this.props
const newList = list.toJS() //toJS方法的使用
7-5 使用 redux-immutable 統(tǒng)一數(shù)據(jù)格式

上一小節(jié)說(shuō)到望抽,我們將state初始化的數(shù)據(jù)通過(guò)immutable這個(gè)庫(kù)變成了immutable對(duì)象加矛,確保了state里面數(shù)據(jù)的穩(wěn)定性,但是呢煤篙,在我們組件去獲得immutable的時(shí)候:
focused: state.header.get('focused')中
state.header是“js對(duì)象”
而后面的.get('focused')則是“immutable對(duì)象”
這樣看的有些不統(tǒng)一斟览,那么如何把state.header也變成immutable對(duì)象呢?那么我們就去看那個(gè)地方設(shè)置state.header

//安裝redux-immutable
npm install redux-immutable -S

//在最外層的reducer.js 文件對(duì)跟reducer進(jìn)行設(shè)置
將
import {combineReducers} from "redux";
改為
import {combineReducers} from "redux-immutable";//redux 改為 redux-immutable
import {reducer as headerReducer} from '../common/header/store'

const reducer = combineReducers({
    header: headerReducer
});
export default reducer;


//優(yōu)化代碼
const mapState = (state) => {
    return {
        focused: state.get(header).get('focused')
    }
};
改為 連續(xù)調(diào)用兩次get方法通過(guò)getIn方法一次實(shí)現(xiàn)
const mapState = (state) => {
    return {
        focused: state.getIn(['header','focused'])
    }
};

ps:學(xué)習(xí)了解更多immutable的知識(shí)鏈接

7-6 避免無(wú)意義的請(qǐng)求發(fā)送辑奈,提升組件性能

有些數(shù)據(jù)并不是我們每次點(diǎn)擊就去請(qǐng)求接口苛茂,需要我們初次點(diǎn)擊的時(shí)候,請(qǐng)求一次接口鸠窗,隨后點(diǎn)擊就不請(qǐng)求了妓羊,那么就要加一些判斷限制一下

const {list} = this.props

//當(dāng)去請(qǐng)求一個(gè)列表的時(shí)候,如果初始的數(shù)據(jù)為0稍计,那么我去請(qǐng)求一次接口
(list.length === 0) && dispatch(action)

ps:這種只是項(xiàng)目中的一種情況躁绸,我們?cè)陂_發(fā)過(guò)程中呢,要結(jié)合項(xiàng)目開發(fā)功能臣嚣,來(lái)寫不同的判斷來(lái)減少或者沒(méi)必要的接口請(qǐng)求
7-7 什么是路由涨颜,如何在React中使用路由功能

我們使用的是react-router-dom來(lái)做路由的

//安裝 react-router-dom
npm install react-router-dom -S

//使用
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import {BrowserRouter, Route} from 'react-router-dom';
import store from './store';
import {GlobalStyle} from './style';
import {GlobalStyle2} from './statics/font/iconfont'
import Header from './common/header';
import Home from './pages/Home';
import Detail from './pages/Detail';

class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <GlobalStyle/>
                <GlobalStyle2/>
                <Header/>
                <BrowserRouter>
                    <div>
                        <Route path='/' exact component={Home}></Route>
                        <Route path='/detail' exact component={Detail}></Route>
                    </div>
                </BrowserRouter>
            </Provider>
        )
    }
}
export default App;

2.單頁(yè)路由的跳轉(zhuǎn),通過(guò)Link 的to屬性進(jìn)行頁(yè)面的跳轉(zhuǎn)
單頁(yè)跳轉(zhuǎn)的好處,減少http請(qǐng)求茧球,請(qǐng)求速度會(huì)很快

import {Link} from 'react-router-dom';
<Link to='/'>
  <Logo />
</Link>

3.頁(yè)面路由參數(shù)的傳遞
1)庭瑰、動(dòng)態(tài)路由獲取參數(shù)
http://localhost:3000/detail/1

//配置路由
<Route path='/detail/:id' exact component={Detail}></Route>

//獲取Url上面的1這個(gè)參數(shù)值
this.props.match.params.id

2)、非動(dòng)態(tài)路由獲取參數(shù)
http://localhost:3000/detail?id=1

//配置路由
<Route path='/detail' exact component={Detail}></Route>

//獲取Url上面的1這個(gè)參數(shù)值
const id = this.props.location.search;   //search: ?id=2
再對(duì)id進(jìn)行數(shù)據(jù)的處理才能拿到最終我們想要的值
7-8 PureComponent 的應(yīng)用
import React,{Component} from 'react';
改為 
import React,{PureComponent} from 'react';

為了考慮react的性能優(yōu)化抢埋,需要我們對(duì)變更的數(shù)據(jù)的進(jìn)行監(jiān)測(cè)弹灭,采用shouldComponentUpdate對(duì)組件進(jìn)行優(yōu)化,如果這樣的話揪垄,就需要寫很多shouldComponentUpdate的代碼穷吮,那么react中這個(gè)PureComponent組件 就是自動(dòng)幫助我們做這部分優(yōu)化功能的。
注釋:如果采用PureComponent這個(gè)組件那么饥努,必須保證你的數(shù)據(jù)是immutable的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末捡鱼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子酷愧,更是在濱河造成了極大的恐慌驾诈,老刑警劉巖缠诅,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乍迄,居然都是意外死亡管引,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門闯两,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)褥伴,“玉大人,你說(shuō)我怎么就攤上這事漾狼≈芈” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵逊躁,是天一觀的道長(zhǎng)似踱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)志衣,這世上最難降的妖魔是什么屯援? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任猛们,我火速辦了婚禮念脯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弯淘。我一直安慰自己绿店,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布庐橙。 她就那樣靜靜地躺著假勿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪态鳖。 梳的紋絲不亂的頭發(fā)上转培,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音浆竭,去河邊找鬼浸须。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邦泄,可吹牛的內(nèi)容都是我干的删窒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼顺囊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肌索!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起特碳,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诚亚,失蹤者是張志新(化名)和其女友劉穎晕换,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亡电,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡届巩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了份乒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恕汇。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖或辖,靈堂內(nèi)的尸體忽然破棺而出瘾英,到底是詐尸還是另有隱情,我是刑警寧澤颂暇,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布缺谴,位于F島的核電站,受9級(jí)特大地震影響耳鸯,放射性物質(zhì)發(fā)生泄漏湿蛔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一县爬、第九天 我趴在偏房一處隱蔽的房頂上張望阳啥。 院中可真熱鬧,春花似錦财喳、人聲如沸察迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扎瓶。三九已至,卻和暖如春泌枪,著一層夾襖步出監(jiān)牢的瞬間概荷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工碌燕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留误证,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓陆蟆,卻偏偏與公主長(zhǎng)得像雷厂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叠殷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容