本文是自己最近再学习React的总结和记录。尽力把每个示例代码都写了出来,尽力写的通俗易懂一些,也算是锻炼自己的讲述能力,据说给别人讲的话学的会更快,所以写下此文。
如有错误或者其他,还望各位批评指正
还没整理完全,趁着没开学,我会抓紧时间学习和更新
在JS里面写的一些标签称为JSX语法
在返回的时候不需要加引号
import React from 'react'; import ReactDOM from 'react-dom'; import logo from './logo.svg'; // 基本 const jsx = <h1>hello react</h1> // 变量 const name = '123456' const jsxName = <h1>{ name }</h1> // 函数 const user = { firstName: 'tom', lastName: 'jerry' }; function formatName(user) { return user.firstName + ' ' + user.lastName; } const jsxFunction = <h2>{ formatName(user) }</h2> // 对象 const greet = <p>hello, Jerry</p> const jsxObject = <h2>{ greet }</h2> // 三元表达式 const showTitle = true; const title = showTitle ? <h2>{ name }</h2> : null; const jsxOthers = (<div>{ /* 条件语句句 */ } { title } </div>); // 循环 const arr = [1, 2, 3].map(num => <li key={ num }>{ num } </li>) const jsxArray = (<div> {/* 数组 */ } <ul>{ arr }</ul> </div>) // 注意 const jsxAtt = (<div> { /* 属性:静态值⽤用双引号,动态值⽤用花括号;class、for等 要特殊处理理。 * 像class需要写成className,for需要写成htmlFor,并且属性名需要采用驼峰命名法 * */ } <img src={ logo } style={ { width: 100 } } className="img"/></div>) // 函数传参 function greeting(name) { if (name) { return <h1>Hello, {name}!</h1>; } return <h1>Hello, Stranger.</h1>; } let name2 = '测试'; const element = greeting(name2); // 循环 let names = ['张三','李四','王五']; let elements = []; for(let i=0;i<names.length;i++){ elements.push(<li>{names[i]}</li>); } export { jsx, jsxName, jsxFunction, jsxObject, jsxOthers, jsxArray,jsxAtt,element,elements } 复制代码
可以将UI切分成一些独立的、可复用的部件,
class组件通常拥有状态和生命周期,继承于Component,实现 render方法
import React, { Component } from 'react'; class Home extends Component { render() { return <h1>Hello, Class Components</h1>; } } 复制代码
import React, { Component } from 'react'; class State extends Component { // 使用state属性维护状态,在构造函数中初始化状态 constructor(props) { super(props); this.state = { date: new Date(), number: 0 }; // this.changeNumber = this.changeNumber.bind(this) } // 组件挂载时启动定时器每秒更新状态 componentDidMount() { this.timerID = setInterval(() => { // 使⽤用setState方法更新状态 this.setState({ date: new Date() }); }, 1000); } // 组件卸载时停止定时器 componentWillUnmount() { clearInterval(this.timerID); } // 这里不绑定this 会报错 两种方法 第一种 用箭头函数。第二种在constructor进行绑定声明 // setState是异步执行的。执行这个结果是 log 是2不是3 证明是异步执行 changeNumber = () => { this.setState({ number: this.state.number + 1 } ) this.setState({ number: this.state.number + 2 } ) // 这里不能进行直接修改 console.log(this.state.number) } // 同步改变 changeNumber2 = ()=> { this.setState((nextState)=>{ return { number: nextState.number + 1 } } ) this.setState((nextState)=>{ return { number: nextState.number + 2 } } ) console.log(this.state.number) // 使用定时器 // setTimeout(() => { this.setState((nextState)=>{ return { number: nextState.number + 1 } } ); console.log(this.state.counter); }, 0); // 原生事件中修改状态 // componentDidMount(){ document.body.addEventListener('click', this.setState((nextState)=>{ return { number: nextState.number + 1 } } ), false) } } render() { return ( <div> { this.state.date.toLocaleTimeString() } <p>{ this.state.number }</p> <p>setState是异步执行的</p> <button onClick={this.changeNumber}>异步改变number</button> <button onClick={this.changeNumber2}>同步改变number</button> </div> ) } } 复制代码
在constructor
设置状态。使用setState
来改变状态。示例如上
注意,setState是 异步的!!!!
如何证明?
见示例代码中changeNumber
的描述和实现
常见的有三种方法:
上述三种方法在示例代码中都可以找到。
一般有两种方法
constructor
里面用bind
方法绑定。例如this.changeNumber = this.changeNumber.bind(this)
changeNumber = () => { // 执行代码 }
。(**原因解释:**箭头函数默认指向定义它时,所处上下文的对象的this指向)import React from "react"; import User from "./pages/User"; function App() { return ( <div> <User /> </div> ); } export default App; 复制代码
后文的 react HOOKs
会做详细解释
适用于父子组件通讯
// index.js ReactDOM.render(<App title="测试代码" />, document.querySelector('#root')); // App.js <h2>{this.props.title}</h2> // 输出为 测试代码 复制代码
官网原话 :组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props
官方解释: 通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
实例代码
import React from 'react' class Child_1 extends React.Component{ constructor(props){ super(props) } render(){ return ( <div> <h1>{this.props.value+'child 1'}</h1> </div> ) } } class Child_2 extends React.Component{ constructor(props){ super(props) } render(){ return ( <div> <h1>{this.props.value+'child 2'}</h1> </div> ) } } class Parent extends React.Component { constructor(props){ super(props) this.state = { txt:"我是父组件" } this.handleChange = this.handleChange.bind(this) } handleChange(e){ this.setState({ txt:e.target.value }) } render(){ return ( <div> <input type="text" value={this.state.txt} onChange={this.handleChange}/> <p>{this.state.txt}</p> <Child_1 value={this.state.txt}/> <Child_2 value={this.state.txt}/> </div> ) } } export default Parent 复制代码
跨层级组件之间通信
// ContextFather.js import React from 'react'; import ContextChild from './child'; const Context = React.createContext() const Provider = Context.Provider const Consumer = Context.Consumer const data = { name: '我是father数据', } function ContextApp() { return ( <div> <Provider value={ data }> <Consumer> { value => <ContextChild {...value}/> } </Consumer> </Provider> </div> ) } export default ContextApp // child.js import React, { Component } from 'react'; class ContextChild extends Component { constructor(props) { super(props); } render() { console.log(this.props) return (<h1>hello {this.props.name}</h1>); } } export default ContextChild 复制代码
这样可以获取到传递过来的name
值
// Context.js import React from 'react'; const Context = React.createContext() const Provider = Context.Provider const Consumer = Context.Consumer export {Provider,Consumer} // ContextFather import React from 'react'; import ContextChild from './child'; import {Consumer,Provider} from './Context'; const data = { name: '我是father数据', } function ContextApp() { return ( <div> <Provider value={ data }> // 这个层级不需要就不用写 Consumer <ContextChild/> </Provider> </div> ) } export default ContextApp // child.js import React, { Component } from 'react'; import ContextChild2 from './child2'; import {Consumer} from './Context'; class ContextChild extends Component { constructor(props) { super(props); } render() { return ( <div> <h1>我是第一层</h1> // 这里获取的是一个空值,没有数据 <h2>{this.props.name}</h2> // 这个层级需要就吧数据传递过去 <Consumer> { value => <ContextChild2 {...value}/> } </Consumer> </div> ) } } export default ContextChild // child2.js import React, { Component } from 'react'; class ContextChild2 extends Component { constructor(props) { super(props); } render() { console.log(this.props) console.log('我是第二层的') return ( <div> <h1>我是第二层的</h1> <h1>{this.props.name}</h1> </div> ); } } export default ContextChild2 复制代码
Context.js
里面的语句。则会获取不到数据。不同的provider提供的值不一致Context
被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。不过,这并非意味着我们不需要关注Context。事实上,很多优秀 的React组件都通过Context来完成⾃自己的功能,比如react-redux没学过Vue的可以理解为全局状态管理,即你在哪里都能访问的到(我个人是这么理解的)
解释: reducer 就是一个纯函数,接收旧的 state
和 action
,返回新的 state
本例子来源于MDN
const array1 = [1, 2, 3, 4]; const reducer = (total, currentValue) => total + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15 复制代码
compose
就是执行一系列的任务(函数)
如何输出1 2 3
function f1() { console.log("1"); } function f2() { console.log("2"); } function f3() { console.log("3"); } 复制代码
第一种
f1();f2();f3();
第二种
f3(f2(f1()))
第三种
function compose(...funcs) { // funcs 是一个函数列表 if (funcs.length === 0) { console.log('empty'); } else if (funcs.length === 1) { return funcs[0]; } else { return funcs.reduce( (left, right) => (...args) => { // console.log(args) // console.log(right) // console.log(left) right(left(...args)) // 相当于 f3(f2(f1())) } ); } } compose(f1,f2,f3)() 复制代码
store
来存储数据store
里reducer
初始化state
并定义state
修改规则dispatch
一个action
来提交对数据的修改action
提交到reducer
函数里,根据传入的action
的type
,返回新的 state
实例代码
// 创建store store/ReduxStore import { createStore } from 'redux'; // 创建数据并指定行为 const counterReducer = (state = 0, action) => { switch (action.type) { case 'add': return state + 1 case 'minus': return state - 1 default: return state } } // 创建一个store const store = createStore(counterReducer) export default store 复制代码
// ReduxPage import React, { Component } from 'react'; import store from '../store/ReduxStore'; export default class ReduxPage extends Component { componentDidMount() { // 挂在之后 store.subscribe(() => { // 执行订阅 console.log('subscribe'); this.forceUpdate(); // 重新render //this.setState({}); // 这个也可以,内部方法也是forceUpdate }); } // 执行行为 add = () => { // 派发action store.dispatch({ type: 'add' }); }; minus = () => { store.dispatch({ type: 'minus' }); }; stayStatic = () => { store.dispatch({ type: 'others' }); }; render() { console.log('store', store); return ( <div> <h1>ReduxPage</h1> <p>获取store数据</p> <p>{ store.getState() }</p> <button onClick={ this.add }>+</button> <button onClick={ this.minus }>-</button> <button onClick={ this.stayStatic }>+-</button> </div> ); } } 复制代码
如果点击后 数据没有更新。则是订阅状态没有改变(也就是没有render
)
createStore
创建store
reducer
初始化、修改状态函数getState
获取状态值dispatch
提交更新subscribe
变更订阅以React
的方式写来Redux
提供了两个api
Provider
为后代组件提供store
(类似Context
)connect
为组件提供数据和变更⽅方法原来的只可以同步执行,安装之后,可以进行异步操作
只可以再开发环境下使用。用来输出各个action
大家会发现其实还是有 redux
的引入,说明react-redux
也是基于Redux
import { createStore, applyMiddleware,combineReducers } from 'redux'; import logger from 'import React, { Component } from 'react'; import { connect } from 'react-redux'; class ReactReduxPage extends Component { render() { const { num, add, minus,asyAdd } = this.props; // console.log(this.props) // 原本这里面什么都没有,再进行connect映射后出现了 return ( <div> <h1>ReactReduxPage</h1> <p>{ num }</p> <button onClick={ add }>+</button> <button onClick={ minus }>-</button> <button onClick={ asyAdd }>asyAdd</button> </div> ); } } const mapStateToProps = state => { console.log(state) return { num: state }; // return { num: state.counter };// 加了combineReducers 之后需要这么做 因为是一个对象 }; const mapDispatchToProps = { add: () => { return { type: 'add' }; }, minus: () => { return { type: 'minus' }; }, asyAdd: ()=> dispatch =>{ setTimeout(()=>{ dispatch ({type: 'add'}) },1000) } }; export default connect( mapStateToProps,//状态映射 mapStateToProps mapDispatchToProps,//派发事件映射 )(ReactReduxPage) '; import thunk from 'redux-thunk'; const counterReducer = (state = 0, action) => { switch (action.type) { case 'add': return state + 1 case 'minus': return state - 1 default: return state } } // const store = createStore(counterReducer) // const store = createStore(combineReducers({counter: counterReducer}), applyMiddleware(logger, thunk)) const store = createStore(counterReducer, applyMiddleware(logger, thunk)) export default store 复制代码
applyMiddleware
提供扩展中间件(有顺序,这个事先执行logger,在执行thunk)
combineReducers
命名空间,当你有多个store的时候,使用这个,就可以用对象的方式访问各个store
import React, { Component } from 'react'; import { connect } from 'react-redux'; class ReactReduxPage extends Component { render() { const { num, add, minus,asyAdd } = this.props; // console.log(this.props) // 原本这里面什么都没有,再进行connect映射后出现了 return ( <div> <h1>ReactReduxPage</h1> <p>{ num }</p> <button onClick={ add }>+</button> <button onClick={ minus }>-</button> <button onClick={ asyAdd }>asyAdd</button> </div> ); } } const mapStateToProps = state => { console.log(state) return { num: state }; // return { num: state.counter };// 加了combineReducers 之后需要这么做 因为是一个对象 }; const mapDispatchToProps = { add: () => { return { type: 'add' }; }, minus: () => { return { type: 'minus' }; }, asyAdd: ()=> dispatch =>{ setTimeout(()=>{ dispatch ({type: 'add'}) },1000) } }; export default connect( mapStateToProps,//状态映射 mapStateToProps mapDispatchToProps,//派发事件映射 )(ReactReduxPage) 复制代码
当然,如果嫌弃太乱,可以单独抽离出来放。最后导入进来就可以
这个也是官方推荐的做法: 我们推荐使用组合而非继承来实现组件间的代码重用。
学过Vue的同学,我个人理解为类似于Vue的插槽
// Layout.js import React, { Component } from 'react'; import TabBar from '../components/TabBar'; class Layout extends Component{ constructor(props) { super(props); } componentDidMount() { const {title = '首页'} = this.props document.title = title } // 兼容具名和不具名 compatible compatibleNamed(children) { let doc = [] if (children.$$typeof) { doc.push(children) } else { for (let item in children){ doc.push(children[item]) } } return doc } render() { console.log(this.props) return ( <div> <h1>我是布局页面</h1> {/* 不具名的使用*/} {/*{*/} {/* this.props.children*/} {/*}*/} {/* 这个是具名的使用*/} { this.props.children.btn } <h2>兼容过后</h2> {this.compatibleNamed(this.props.children).map((item,index)=>{ return ( <div key={index}> {item} </div> ) })} { this.props.showTab && <TabBar/> } </div> ) } } export default Layout // Home.js import React, { Component } from 'react'; import Layout from '../ComponentCombination/Layout'; class Home extends Component { render() { return ( <DisNamed/> ) } } function Named(props) { return ( <Layout showTab={ false } title='商城首页'> <h1>我是首页</h1> </Layout> ) } function DisNamed() { return ( <Layout showTab={ false } title='商城首页'> { { btn: <button>我是按钮</button> } } </Layout> ) } export default Home // TabBar.js import React, { Component } from 'react'; class TabBar extends Component{ render() { return ( <div> <h1>我是TabBar</h1> </div> ) } } export default TabBar 复制代码
上面例子容易混淆,写一个简单点的
import React, { Component } from 'react'; import TabBar from '../components/TabBar'; class Layout extends Component{ constructor(props) { super(props); } render() { console.log(this.props) return ( <div> <h1>我是布局页面</h1> {/* 不具名的使用*/} { this.props.children } { this.props.showTab && <TabBar/> } </div> ) } } export default Layout 复制代码
this.props.children
所展示的是两个Layout
之间的内容
例如
<Layout> <h1>hello</h1> </Layout> 复制代码
this.props.children
展示的就是<h1>hello</h1>
//Layout import React, { Component } from 'react'; import TabBar from '../components/TabBar'; class Layout extends Component{ constructor(props) { super(props); } render() { console.log(this.props) return ( <div> <h1>我是布局页面</h1> {/* 这个是具名的使用*/} { this.props.children.btn } { this.props.showTab && <TabBar/> } </div> ) } } export default Layout // Home.js import React, { Component } from 'react'; import Layout from '../ComponentCombination/Layout'; class Home extends Component { render() { return ( <Named/> ) } } function DisNamed() { return ( <Layout showTab={ false } title='首页'> { { btn: <button>我是按钮</button> } } </Layout> ) } export default Home 复制代码
在传递具名组件的时候,需要用双{{}}
包裹。使用的就当属性使用就可以了
具名 和 不具名 的差异在于传递过来的时候this.props.children
是否含有$$typeof
属性。如果不含有,则是具名。反之,含有则是不具名
如何兼容?
知道了两者差异。就从差异下手
compatibleNamed(children) { let doc = [] if (children.$$typeof) { // 如果不是具名 直接放到数组里 doc.push(children) } else { for (let item in children){ // 如果是具名,则便利children。把里面的内容放进数组里 doc.push(children[item]) } } return doc // 返回的是一个数组 } // 使用 render(){ return ( {this.compatibleNamed(this.props.children).map((item,index)=>{ return ( <div key={index}> {item} </div> ) })} } 复制代码
高阶组件是一个工厂函数,它接收一个组件并返回另一个组件。
简单例子
function Child() { return ( <div> child </div> ) } // Cmp是组件 props 是组件的props const foo = Cmp => props =>{ return ( <div style={{boder: '1px solid #ccc'}}> <Cmp {...props}/> </div> ) } function APP() { const Foo = foo(Child) return ( <div> <Foo/> </div> ) } 复制代码
创建 高阶组件
import { Consumer } from './Context'; import React from 'react'; //Cum 是组件 props是组件传递的props const HOC = Cum => props => { return ( <Consumer> { value => <Cum { ...props } { ...value }/> } </Consumer> ) } export default HOC 复制代码
原来的 Context写法
import React, { Component } from 'react'; import ContextChild2 from './child2'; import HOC from './Hoc Hooks'; import {Consumer} from './Context'; class ContextChild extends Component { constructor(props) { super(props); } render() { return ( <div> <h1>我是第一层</h1> <h2>{this.props.name}</h2> <Consumer> { value => <ContextChild2 {...value}/> } </Consumer> </div> ) } } export default ContextChild 复制代码
改用HOC(高阶组件)之后
// child import React, { Component } from 'react'; import ContextChild2 from './child2'; import HOC from './Hoc Hooks'; class ContextChild extends Component { constructor(props) { super(props); } render() { return ( <div> <h1>我是第一层</h1> <h2>{this.props.name}</h2> {/*直接调用*/} <ContextChild2/> </div> ) } } export default HOC(ContextChild) // child2.js import React, { Component } from 'react'; import HOC from './Hoc Hooks'; class ContextChild2 extends Component { constructor(props) { super(props); } render() { console.log(this.props) console.log('我是第二层的') return ( <div> <h1>我是第二层的</h1> <h1>{this.props.name}</h1> </div> ); } } export default HOC(ContextChild2) 复制代码
原本需要写Consumer
现在只需要一个函数就解决了。非常方便
可能还是会有点迷瞪。我在详细的说一次
import { Consumer } from './Context'; import React from 'react'; const HOC = Cum => props => { return ( <Consumer> { value => <Cum { ...props } { ...value }/> } </Consumer> ) } export default HOC 复制代码
Cum
是组件,props
是组件的所携带的参数,value
是provider
所分发的数据.
类比一下下面的.就是把组件当作了参数,通过传入一个组件,返回另一个新的组件
<Consumer> { value => <ContextChild {...value}/> } </Consumer> 复制代码
// ContextFather.js import React from 'react'; import ContextChild from './child'; import {Consumer,Provider} from './Context'; const data = { name: '我是father数据', } function ContextApp() { return ( <div> <Provider value={ data }> {/*<Consumer>*/} {/* {*/} {/* value => <ContextChild {...value}/>*/} {/* }*/} {/*</Consumer>*/} <ContextChild/> </Provider> </div> ) } export default ContextApp // child.js import React, { Component } from 'react'; import ContextChild2 from './child2'; import HOC from './Hoc Hooks'; class ContextChild extends Component { constructor(props) { super(props); } render() { return ( <div> <h1>我是第一层</h1> <h2>{this.props.name}</h2> <ContextChild2/> </div> ) } } export default HOC(ContextChild) // child2.js import React, { Component } from 'react'; import HOC from './Hoc Hooks'; class ContextChild2 extends Component { constructor(props) { super(props); } render() { console.log(this.props) console.log('我是第二层的') return ( <div> <h1>我是第二层的</h1> <h1>{this.props.name}</h1> </div> ); } } export default HOC(ContextChild2) 复制代码
react-router包含3个库,react-router、react-router-dom和react-router-native。
react-router提供最 基本的路由功能,实际使用的时候我们不会直接安装react-router,而是根据应用运行行的环境选择安装
react-router-dom(在浏览器器中使⽤用)
react-router-native(在rn中使用)。
react-router-dom和 react-router-native都依赖react-router,所以在安装时,react-router也会⾃自动安装
npm install --save react-router-dom
常用 BrowserRouter 、链接-Link、路由-Route、独占路由Switch、重 定向路由-Redirect
// RouterPage import React, { Component } from 'react'; import { BrowserRouter, Link, Route,Switch } from 'react-router-dom'; import HomePage from './HomePage'; import UserPage from './UserPage'; import SearchPage from './Search'; import Login from './Login'; export default class RouterPage extends Component { render() { return ( <div> <h1>RouterPage</h1> <BrowserRouter> <nav> <Link to="/">首页 </Link> <Link to="/user">用户中心 </Link> <Link to="/login">登陆 </Link> </nav> {/* 根路路由要添加exact,实现精确匹配 不加这个可能会出现多次渲染 */ } <Switch> {/*匹配到之后就不在继续往下匹配 Switch作用*/} <Route exact path="/" component={ HomePage }/> {/*<Route path="/user" component={ UserPage }/>*/} <Route path="/login" component={ Login }/> {/*404 页面 一定要放到最后*/} <Route component={() => <h1>404</h1>} /> </Switch> </BrowserRouter> </div> ); } } 复制代码
// RouterPage <Link to="/search/123">详情 </Link> <Route path="/search/:id" component={SearchPage} /> // Search import React from 'react'; function SearchPage(props) { console.log(props) // 有三个 match history location return( <div> <p>通过这样获取到传递的参数</p> SearchPage{props.match.params.id} </div> ) } export default SearchPage 复制代码
案例:比如你需要在登陆之后才能进入到用户页面,这时候就需要用路由守卫。
熟悉Vue的同学应该能知道我想要表达的意思
//RouterPage ... import RouterGuard from './RoutingGuard'; ... <Link to="/user">用户中心 </Link> <Link to="/login">登陆 </Link> <RouterGuard path ='/user' isLogin={true} Cmp={UserPage}/> ... 复制代码
// RouterGuard import React from 'react'; import { connect } from 'react-redux'; import { Route, Redirect } from 'react-router-dom'; function RouterGuard(props) { const { Cmp, path, isLogin, ...rest } = props console.log(rest) // 一些剩下的参数 return ( <Route { ...rest } render={ prop =>{ console.log(prop) // 一些路由的参数 return isLogin ? (<Cmp { ...prop } />) : (<Redirect to={ { pathname: '/login', redirect:props.location.pathname} }/> )} } > </Route> ) } export default connect( state => { return { isLogin: state.user.isLogin, } }, )(RouterGuard) 复制代码
这里面主要是用了渲染函数 render
, 还有一个三元表达式
// LoginStore 记得用provider 包裹APP import { createStore, applyMiddleware, combineReducers } from 'redux'; import logger from 'redux-logger'; import thunk from 'redux-thunk'; const initialLogin = { isLogin: false, name: null }; const userInfo = (state = {...initialLogin}, action) => { switch (action.type) { case 'getUser': return { ...state,isLogin: false } case 'LoginSuccess': return { ...state,isLogin: true,name: '阿琛' } case 'LoginFail': return { ...initialLogin } default: return state } } const store = createStore( combineReducers({ user: userInfo }), applyMiddleware(logger, thunk)) export default store // Login import React from 'react'; import { connect } from 'react-redux'; import {Redirect} from 'react-router-dom' function Login(props) { const {location,login,isLogin} = props console.log(props) const redirect = location.redirect?location.redirect:'/' if (isLogin){ return <Redirect to={redirect} />; } return ( <div> <h1>LoginPage</h1> <button onClick={login}>登陆</button> </div> ) } export default connect(state => ({ isLogin: state.user.isLogin, }), { login: () => { return { type: 'LoginSuccess' }; }, }, )(Login) 复制代码
前面介绍了好多了,接下来介绍一些还没有介绍的
Redux-saga是Redux的一个中间件,主要集中处理react架构中的异步处理工作,被定义为generator(ES6)的形式,采用监听的形式进行工作。
作用和react-thunk差不多。都是为了解决异步
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函 数完全不同,详细参考参考阮一峰老师讲解
npm install --save redux-saga
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
this
import React,{useState} from 'react'; function Counter() { // useState会返回两个,一个是当前值。一个是改变它的函数 let [state,setState] = useState({num: 0}) let [num,setNum] = useState(0) // 两种方式 看个人喜好 return ( <div> <p>{state.num}</p> <p>{num}</p> <button onClick={()=>{setState({num: state.num +1})}}>+</button> <button onClick={()=>{setNum(num + 1)}}>+</button> </div> ) } 复制代码
// alter 捕获得是点击时候得状态 function Counter2 () { // useState 返回两个,一个是当前状态的属性,一个是修改状态的方法。 let [state, setState] = useState({num: 0}) let changeLater = ()=>{ // 这个alter 会捕获当前得状态。即点击按钮时候得状态 setTimeout(()=>{ alert(state.num) }, 2000) } return ( <div> <p>{ state.num }</p> <button onClick={ () => setState({num: state.num+1}) }>+</button> <button onClick={ changeLater }>changeLater</button> </div> ) } 复制代码
// 改变状态注意 function Counter3 () { // useState 返回两个,一个是当前状态的属性,一个是修改状态的方法。 let [state, setState] = useState({num: 0}) let lazyAdd = ()=>{ // 这个alter 会捕获 当前得状态。即点击按钮时候得状态 setTimeout(()=>{ setState({num: state.num+1}) }, 2000) } let lazyAdd2 = ()=>{ // 这个alter 会捕获之后的状态。 setTimeout(()=>{ setState((state) => ({num: state.num+1})) }, 1000) } return ( <div> <p>{ state.num }</p> <button onClick={ () => setState({num: state.num+1}) }>+</button> <button onClick={ lazyAdd }>lazyAdd</button> <p>如果这样写得话,会获取到点击时候得状态在进行改变</p> <button onClick={ lazyAdd2 }>lazyAdd2</button> <p>这样写,改变了整个state,就可以正常相加了</p> </div> ) } 复制代码
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值
// 惰性state // initialState 初始状态参数只会在组件初始渲染得时候调用,后续被忽略 // 且不会自己整合state,需要自己整合 function Counter4 () { let [state, setState] = useState(function () { console.log(' 初始化') return {num: 0, name:'计算器'} }) return ( <div> <p>{state.num}</p> <button onClick={ () => setState({num: state.num+1}) }>+</button> <p>只会输出一次log</p> <p>React.StrictMode 模式下会输出两次log</p> <p>这是展示他不会整合state</p> <p>{ state.name }:{state.num}</p> <p>如果点击上面得button,则name会消失,下面这个则不会</p> <button onClick={ () => setState({...state, num: state.num+1}) }>+</button> </div> ) } 复制代码
按第一个button 计算器消失的原因: setState
不会自己整合State
,需要我们自己手动进行整合