action:動做 {type,.....} 必定要有type 其餘屬性不作限制css
reducer:經過計算產生state
公式:(state,action)=>newStatehtml
store: 容器vue
getState() 獲取全部狀態react
dispatch(action) dispatch裏面能夠跟對象和函數, —— 函數須要單獨處理——中間件
subscribe(監聽函數);—— watchajax
觸發條件:
一、dispatch ---> reducer
二、必須產生一個新的狀態 newStatevuex
exp1:npm
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);
Provider:提供 做用: 把狀態 store共享給全部的子組件 包在
json
connect 用法: connect()(Comp); ---> this.props --> {dispatch}redux
connect(mapStateToProps,xxxxxxx)(Comp); ---> this.props -->{ state, dispatch }
exp1:api
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();
一、安裝 cnpm i -S redux-thunk
二、引入 import thunk from "redux-thunk";
三、redux中引入applyMiddleware
import {createStore,applyMiddleware} from "redux";
四、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:
代碼:
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: