创建项目
npx create-react-app demo
|
绑定事件
import React, {Component} from 'react' import ReactDom from 'react-dom'
class HeaderDom extends Component { this.state = { name: '黄某人', age: 18 }
handleClick1(){ console.log('触发了点击事件'); } handleClick2(){ console.log(this.state.name) }
render() { <div> <button onClick={this.handleClick1}>触发点击事件</button> <button onClick={this.handleClick2.bind(this)}>触发点击事件</button> <div/> } }
|
- 组件名称必须是大写
- 如果事件函数里需要用到this,则在设置事件的时候,需要使用bind绑定this指向
state
state在react中是用来保存组件状态的数据的,类似于vue中的data
获取state
console.log(this.state.xxx);
|
设置state
this.setState({ key: val })
|
组件传参
import React, { Component } from 'react' import '../src/assets/css/app1.css'
class HeaderDom extends Component { render() { return ( <header> <ul className="nav-list"> { this.props.navList.map((nav) => ( <li key={nav.link} title={nav.txt}>{nav.txt}</li> )) } </ul> <h3> {this.props.children} </h3> </header> ) } }
export default class App1 extends Component {
constructor(props) { super(props)
this.state = { navList: [ { txt: '首页', link: '/' }, { txt: '商品', link: '/goods' }, { txt: '发现', link: '/faxian' }, { txt: '社区', link: '/shequ' }, { txt: '我的', link: '/me' }, ] } }
render() { return ( <div> <HeaderDom navList={this.state.navList}>这是children的内容</HeaderDom> </div> ) } }
|
- 父组件通过标签属性的方式传递数据给子组件
- 子组件里通过
this.props.xxx
获取父组件传递过来的参数 - 子组件可通过
this.props.children
获取到标签的内容
props默认值
假如父组件没有传递某些参数给子组件,那么子组件可以这些数据定义一个默认值
import React, { Component } from 'react' import '../src/assets/css/app1.css'
class HeaderDom extends Component { static defaultProps = { navList: [ { txt: '首页1', link: '/' }, { txt: '商品2', link: '/goods' }, { txt: '发现3', link: '/faxian' }, { txt: '社区4', link: '/shequ' }, { txt: '我的5', link: '/me' }, ] }
render() { return ( <header> <ul className="nav-list"> { this.props.navList.map((nav) => ( <li key={nav.link} title={nav.txt}>{nav.txt}</li> )) } </ul> </header> ) } }
export default class App1 extends Component { render() { return ( <div> <HeaderDom navList={this.state.navList} /> </div> ) } }
|
- props的默认值必须在子组件里进行设置
static defaultProps
是固定语法,不能乱写
验证props有效性
验证props参数的合法性,需要安装一个插件
示例代码:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Children extends Component { static propTypes = { title: PropTypes.string }
render() { return ( <h1>{this.props.title}</h1> ) } }
export default class App1 extends Component {
constructor(props) { super(props) this.state = { title: '我是黄某人' } }
render() { return ( <div> <Children title={this.state.title} /> </div> ) } }
|
context传参
示例代码:
import React, { Component } from 'react' import PropTypes from 'prop-types'
class Children extends Component {
static contextTypes = { title: PropTypes.string }
render() { return ( <span>{this.context.title}</span> ) } }
class Child extends Component { render() { return ( <h1> <Children /> </h1> ) } }
export default class App1 extends Component {
constructor(props) { super(props) this.state = { title: '我是黄某人' } }
static childContextTypes = { title: PropTypes.string }
getChildContext() { return { title: this.state.title } }
render() { return ( <div> <Child /> </div> ) } }
|
- 发送方,要写
static childContextTypes
和 getChildContext
- 接收方要写
contextTypes
- 接收方要通过
this.context.xxx
来取出数据 - 参考
https://zh-hans.reactjs.org/docs/context.html
子传父
- 父类将自己身上的某个方法传递给子类
- 子类通过
this.props.xxx()
来得到这个方法 - 当子类被触发某个事件的时候,子类主动去调用这个父类传递过来的方法
- 如果需要传递参数给父类,则通过子类传递实参,父类通过回调函数的形参接收
示例代码:
import React, { Component } from 'react'
class Child extends Component { handleClick() { this.props.fatherMethod(66); } render() { return ( <button onClick={this.handleClick.bind(this)}>点击</button> ) } }
export default class App1 extends Component {
constructor(props) { super(props) this.state = { title: '我是黄某人' } }
fatherMethod(title) { this.setState({ title }) }
render() { return ( <div> <Child fatherMethod={this.fatherMethod.bind(this)} /> <p>{this.state.title}</p> </div> ) } }
|
生命周期
挂载阶段
constructor
:初始化组件UNSAFE_compomentWillMount
:数据挂载前render
:渲染页面componentDidMount
:数据挂载后,一般在这发ajax
更新阶段
- props更新
UNSAFE_componentWillReceiveProps
:接收props前
- state更新
shouldComponentUpdate
:可以得到修改前后的值,可选择是否重新渲染,return true
渲染,return false
不渲染UNSAFE_componentWillUpdate
:更新前componentDidUpdate
:更新后
卸载期
componentWillUnmount
:页面或组件被卸载前
受控和不受控组件
路由
依赖插件:cnpm i react-router-dom
hash路由使用方式:
import ReactDom from 'react-dom'
import { HashRouter, Route } from 'react-router-dom'
import App from './router/App.jsx' import Index from './router/Index.jsx' import List from './router/List.jsx'
let router = <HashRouter> {} <Route path="/" exact component={App}></Route> <Route path="/index" exact component={Index}></Route> <Route path="/list" exact component={List}></Route> </HashRouter>
ReactDom.render(router, document.getElementById('root'))
|
- 导入
HashRouter
、Route
- 导入页面组件
- 声明路由对象
- 配置路由规则
- 渲染路由对象
路由传参
发送方:
<a href="#/app/list/189">跳转</a>
<Route path="/app/list/:listId"/>
|
接收方:
<p> {this.props.routeParams.listId} </p>
|
嵌套路由
import React, { Component } from 'react'
import { HashRouter as Router, Route, Redirect } from 'react-router-dom'
import Index from './Index.jsx' import List from './List.jsx'
class App extends Component { render() { return ( <div> <ul style={{ display: "flex", justifyContent: "center", listStyle: "none" }}> <li style={{ marginRight: 20 }}><a href="#/app/index"> 首页 </a></li> <li style={{ marginRight: 20 }}><a href="#/app/list"> 列表页 </a></li> </ul> <div style={{ margin: "0 auto", width: 1200, height: 500, border: "2px solid red" }}> {} <Route path="/app/index" exact component={Index} /> <Route path="/app/list" exact component={List} /> </div> </div> ) } }
let router = <Router> <div> {} <Redirect from="/" to="/app/index" component={Index}></Redirect> {} <Route path="/app" component={App}></Route> </div> </Router>
export default router
|
- 注意点:
<Route> 这里不能写任何东西,否则路由失效 </Route>
常见问题
- 执行
yarn eject
后报错 React is not defined
- 原因:jsx 运行时的 webpack 配置会在 eject 时候被删除,所以需要在
package.json
把配置加回来
{ "babel": { "presets": [ [ "react-app", { "runtime": "automatic" } ] ] } }
|
body { touch-active: pan-y; }
|
- 使用
react-mobile
的 Carousel
滑动时报错 Unable to preventDefault inside passive event listene
- 原因:
html { touch-active: pan-y; }
|
Hooks
作用:让函数组件也有自己的state
用法:
import {useState} from 'react'
const Demo = () => { const [name, setName] = useState('黄某人'); return (<div>{name}</div>) }
|
作用:类似于 componentDidMount
和 componentDidUpdate
用法:
import {useState} from 'react'
const Demo = () => { useEffect(() => { console.log('页面加载完成执行,数据更新时也执行') }) return (<div>666</div>) }
|
useState
和 useEffect
在组件内可以定义多个
react-redux的用法
新建 reducers
文件夹,并在里面创建 index.js
文件
然后再 reducers
文件夹内创建别的文件夹或文件,根据自己的需要,例如创建 blog.js
blog.js
的内容如下:
const initState = { list: [ {userId: 1, title: '测试标题', body: '测试内容'}, {userId: 2, title: '测试标题', body: '测试内容'}, ], isLoading: false };
export default (state = initState, action) => { switch(action.type) { default: return state; } }
|
在 index.js
里导入我们刚刚创建的好的 blog.js
import { combineReducers } from 'redux' import blog from './blog' export default combineReducers({ blog })
|
在 src
目录下新建 store.js
,内容如下
import { createStore, applyMiddleware } from 'redux' import reducers from './reducers/index' import thunk from 'redux-thunk' export default createStore( reducers, applyMiddleware(thunk) )
|
打开 src/index.js
入口文件,内容如下
import react from 'react' import { render } from 'react-dom' import { provider } from 'react-redux' import App from './App' import store from './store'
render( <provider store={store}> <App/> </provider>, document.getElementById('root') )
|
新建一个页面组件,假设内容如下
import react, {Component} from 'react' import {connect} from 'react-redux'
class Blog extends Component { console.log(this.props) const {list, isLoading} = this.props; render(){ return ( <> { isLoading ? <div> loading··· </div> : this.props.list.map(item=>{ return ( <h1>{item.title}</h1> <p>{item.body}</p> ) }) } </> ) } }
const mapState = state => ({ list: state.blog.list })
export default connect(mapState)(Blog)
|