回顧:Redux: 類似於 Vuex
概念:store/reducer/action
action:動作 {type,.....} 一定要有type 其他屬性不做限制
reducer:通過計算產生state
公式:(state,action)=>newState
store: 容器
getState() 獲取所有狀態
dispatch(action) dispatch里面可以跟對象和函數, —— 函數需要單獨處理——中間件
subscribe(監聽函數);—— watch
觸發條件:
1、dispatch ---> reducer
2、必須產生一個新的狀態 newState
exp1:
1.ction.js
export const PLUS = Symbol("PLUS");
export const MINUS = Symbol("MINUS");
export function plusAction(){
return {type:PLUS};
}
export function minusAction(){
return {type:MINUS};
}
2.reducer.js
import {PLUS} from "./actions"
//數據初始化
const initState = {count:1};
//創建reducer
const reducer = (state = initState,action = {})=>{
const {type} = action;
switch(type){
case PLUS:
return {...state,count:state.count+1};
default:
return state;
}
}
export default reducer;
3.store
//引入
import {createStore} from "redux";
import reducer from "./reducer";
//創建store
const store = createStore(reducer);
store.subscribe(()=>console.log(store.getState()));
export default store;
4.App.js
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<div className="App">
App
</div>
);
}
}
export default App;
5.Counter.js
//引入
import React,{Component} from "react";
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import {plusAction,minusAction} from "./actions";
//創建組件
class Counter extends Component{
render(){
console.log(11111,this.props);
const {count,plusAction} = this.props;
return (
<div>
{count}
<input onClick={plusAction} value="+" type="button"/>
</div>
);
}
}
const mapStateToProps = state =>({
count:state.count
});
function mapDispatchToProps(dispatch){
return bindActionCreators({plusAction,minusAction},dispatch);
}
export default connect(mapStateToProps,mapDispatchToProps)(Counter);
react-redux: {Provider,connect}
Provider:提供 作用: 把狀態 store共享給所有的子組件 包在
connect 用法: connect()(Comp); ---> this.props --> {dispatch}
connect(mapStateToProps,xxxxxxx)(Comp); ---> this.props -->{ state, dispatch }
exp1:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from "react-redux";
import store from "./Counter/store";
import App from "./Counter/App";
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
registerServiceWorker();
異步action —— 在原有的異步函數內 包個函數—— 函數會有一個dispatch參數
在action里面有延遲操作!定時器、數據交互(ajax,fetch...)
用redux-thunk
第一步:
1、安裝 cnpm i -S redux-thunk
2、引入 import thunk from "redux-thunk";
3、redux中引入applyMiddleware
import {createStore,applyMiddleware} from "redux";
4、const store = createStore(reducer,applyMiddleware(thunk));
第二步:
++在異步anctions中使用++
問題1:
++定時器++
function plusAsyncAction(){
setTimeout(()=>{
return {type:"PLUS"};
},1000);
}
通過中間件來解決
function plusAsyncAction(){
return function(dispatch){
setTimeout(()=>{
//自己手動派發action
dispatch({type:"PLUS"});
},1000);
}
}
exp1:
import {createStore,applyMiddleware} from "redux";引入模塊
const store = createStore(reducer,applyMiddleware(thunk)); 使用模塊
import React,{Component} from "react";
import {createStore,applyMiddleware} from "redux";
import thunk from "redux-thunk";
//創建store
//數據初始化
const initState = {count:1};
//創建reducer
const reducer = (state=initState,action={})=>{
const {type} = action;
switch(type){
case "PLUS":
return {...state,count:state.count+1};
default:
return state;
}
}
//創建store
const store = createStore(reducer,applyMiddleware(thunk));
function plusAction(){
return {type:"PLUS"};
}
function plusAsyncAction(){
return function(dispatch,getState){
const val = getState();//獲取是修改之前的狀態
console.log(val,getState);
setTimeout(()=>{
//dispatch({type:"PLUS"});
dispatch(plusAction());
},1000)
}
}
class Counter extends Component{
constructor(...args){
super(...args);
store.subscribe(()=>{
console.log(store.getState());
this.forceUpdate();
});
}
plus(){
store.dispatch(plusAsyncAction());
}
render(){
return (
<div>
{store.getState().count}
<input onClick={this.plus.bind(this)} value="+" type="button"/>
</div>
);
}
}
export default Counter;
res:
延遲一秒顯示
問題2:
++交互(數據交互(ajax,fetch...))++
function fetchAsyncAction(){
fetch(url).then.then(data=>{
return {type:"GET",payload:data};
})
//return undefined
}
通過中間件來解決
function fetchAsyncAction(){
return function(dispatch){
fetch(url).then.then(data=>{
dispatch({type:"GET",payload:data});
})
}
}
exp2:
數據交互
import React,{Component} from "react";
import {createStore,applyMiddleware} from "redux";
import thunk from "redux-thunk";
const url = "http://localhost:9000/api/v2/movie/in_theaters?city=北京";
//創建store
//數據初始化
const initState = {subjects:[]};
//創建reducer
const reducer = (state = initState,action = {})=>{
const {type} = action;
switch(type){
case "GET":
return {...state,subjects:action.payload};
default:
return state;
}
}
//創建store
const store = createStore(reducer,applyMiddleware(thunk));
function fetchSyncAction(){
return function(dispatch){
fetch(url).then(res=>res.json()).then(data=>{
console.log(data.subjects);
dispatch({type:"GET",payload:data.subjects});
})
}
}
//創建組件
class Fetch extends Component{
constructor(...args){
super(...args);
store.subscribe(()=>{
console.log(store.getState());
this.forceUpdate();
})
}
asyncFecth(){
store.dispatch(fetchSyncAction());
}
fn(){
//url:"http://api.douban.com/v2/movie/in_theaters?city=北京",
//url:"http://localhost:9000/api/v2/movie/in_theaters?city=北京",
//store.dispatch(plusAsyncAction());
fetch(url).then(res=>res.json()).then(data=>{
console.log(data.subjects);
store.dispatch({type:"GET",payload:data.subjects});
})
}
render(){
return (
<div>
{
store.getState().subjects.map(({id,title})=>{
return <div key={id}>{title}</div>;
})
}
<input onClick={this.fn.bind(this)} value="普通fetch" type="button"/>
<input onClick={this.asyncFecth.bind(this)} value="asyncFecth" type="button"/>
</div>
);
}
}
export default Fetch;
res:
dispatch(action)
View -------------------> reducer ---------> newState
異步 同步
dispatch(asyncAction) dispatch(action)
View ----------------------> middleware攔截 ---------------> reducer --------> newState
屬性驗證:
安裝:cnpm i -D prop-types
參考:https://reactjs.org/docs/typechecking-with-proptypes.html?#___gatsby
Test.propTypes = {
屬性:驗證規則
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
}
exp1:
import React, { Component } from "react";
import PropTypes from "prop-types";
class Test extends Component {
//判斷name是否是字符串,若不是字符串則報錯
//,isRequired表示空也判斷
//寫法二
static propTypes = {
name: PropTypes.string.isRequired,
}
render() {
const {name} = this.props;
return (
<div>屬性驗證:name: {name}</div>
);
}
}
//寫法一
/*Test.propTypes = {
name: PropTypes.string.isRequired,
}*/
export default Test;
res:
符合判定不返回任何值,不符合則會報錯,並提醒輸入的是何種類型數據.
exp2:
自定義屬性驗證
import React, { Component } from "react";
// import PropTypes from "prop-types";
function Test(props){
return (
<div>屬性驗證:name: {props.name}</div>
);
}
const PropTypes = {
string:function(props, propName, componentName){
if(typeof props[propName] !== "string"){
return new Error(`你輸入的${propName}我期望的是 字符串 ,但是你給我的是 ${typeof props[propName]} `);
}
}
}
Test.propTypes = {
name:PropTypes.string,
}
export default Test;
res:
在異步anctions中使用的案例
豆瓣網:
代碼:
1.App.js
import React, { Component } from "react";
import { Provider} from "react-redux";
import store from "./store";
import MoveBox from "./MoveBox";
import "./index.css";
let arr = [
{id:1,city:"北京"},
{id:2,city:"上海"},
{id:3,city:"深圳"},
{id:4,city:"青島"}
];
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="App">
<MoveBox arr={arr}/>
</div>
</Provider>
);
}
}
export default App;
-----------------------------------
2.index.css
.active{background:pink;}
-----------------------------------
3.reducer.js
import {QUERY} from "./actions"
const initState = {
id:1,
city:"北京"
};
export default (state = initState,action = {})=>{
switch(action.type){
case QUERY:
return {...state,id:action.payload.id,city:action.payload.city};
default:
return state;
}
};
-------------------------------
4.store.js
import {createStore, applyMiddleware} from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const store = createStore(reducer,applyMiddleware(thunk));
//測試用
store.subscribe(()=>{
console.log("store:",store.getState())
});
export default store;
----------------------------------
5.action.js
export const QUERY = Symbol("QUERY");
------------------------------------
6.MoveBox.js
import React, { Component } from "react";
import { connect } from "react-redux";
import MoveTitle from "./MoveTitle";
import MoveList from "./MoveList";
//url:"http://api.douban.com/v2/movie/in_theaters?city=北京",
//url:"http://localhost:9000/api/v2/movie/in_theaters?city=北京",
class MoveBox extends Component {
render() {
console.log("this.props:",this.props);
const {id,city,arr} = this.props;
return (
<div>
<MoveTitle id={id} arr={arr}/>
<MoveList city={city} />
</div>
);
}
}
function mapStateToProps(state){
return {id:state.id,city:state.city};
}
export default connect(mapStateToProps)(MoveBox);
-------------------------------------------
7.MoveTitle.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { QUERY } from "./actions";
class MoveTitle extends Component {
fn(item){
this.props.dispatch({type:QUERY,payload:item});
}
render() {
const {id,arr} = this.props;
return (
<ul>
{
arr.map(item=>{
return <input onClick={this.fn.bind(this,item)} className={item.id === id?"active":""} key={item.id} type="button" value={item.city}/>
})
}
</ul>
);
}
}
export default connect()(MoveTitle);
--------------------------------------
8.MoveList.js
import React, { Component } from "react";
//http://localhost:9000/api/v2/movie/in_theaters?city=北京",
const url = "http://localhost:9000/api/v2/movie/in_theaters?city=";
class MoveList extends Component {
state = {title:this.props.city,subjects:[]};
UNSAFE_componentWillReceiveProps(props){
console.log("UNSAFE_componentWillReceiveProps",props,this.props);
this.featcData(props.city);
}
componentDidMount(){
this.featcData();
}
featcData(city){
city = city||this.props.city
fetch(`${url}${city}`).then(res=>res.json()).then(data=>{
console.log(111,data);
this.setState(data);
});
}
render() {
return (
<div>
{this.state.title}
<hr />
<ul>
{
this.state.subjects.map(item=>{
return <li key={item.id}>{item.title}</li>
})
}
</ul>
</div>
);
}
}
export default MoveList;
res: