生命周期
-
getDefaultProps,通过这个方法初始化props属性,props来自于父组件,以及其他组件
-
getInitialState,初始化当前组件的状态,这个状态会贯穿整个项目,数据变化都取决于setState
-
componentWillMount,在组件加载和初始化之前调用
-
render ,react中最重要的方法,react元素的渲染都取决于render
-
componentDidMount,react DOM加载完会调用这个方法
-
componentWillReceiveProps,来自父组件属性的传递调用的方法
-
shouldComponentUpdate,组件的更新,只要调用setState就会调用这个生命周期钩子
-
componentWillUpdate,组件更新之前调用的方法
-
componentDidUpdate ,组件更新之后调用的方法
-
componentWillUnmount,组件销毁调用的方法
创建项目命令
npm install -g create-react-app
create-react-app my-app
reate-redux
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
如果想要了解详情可以查看官方网www.redux.org.cn/ 下面截图测试代码文件格式如下
Provider
index.js
import React from 'react'
import ReactDOM from 'react-dom'
// ## Provider 提供者配合 connect 链接器
import { Provider } from 'react-redux'
// 跟vue类似引入store【初始状态和方法】
import store from './store'
import App from './App'
// Provider 包裹APP
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
APP.js
import React from 'react'
import { connect } from 'react-redux'
import { decrementActionCreator, incrementActionCreator,resetCreator } from './actions/counter'
function App(props) {
return (
<div>
{ props.count }
<button onClick={props.plus}>加数量</button>
<button onClick={props.minus}>减数量</button>
<button onClick={props.reset}>我要将它重置为0</button>
</div>
)
}
/**
* 将 store 中 state 的数据映射到组件的属性中
* mapStateToProps 是一个函数,会传递 store 中的 state 作为参数。
* 返回一个普通对象,该对象会被合并到组件的 props 中
* @param {*} state
* @returns
*/
const mapStateToProps = state => ({
count: state.counter.count,
num: state.counter.num,
})
/**
* 将更新状态数据的动作映射到组件的属性中
* mapDispatchToProps 是一个函数,会传递 dispatch 作为参数。
* 返回一个对象,返回对象中的各字段应该是函数(方法),返回
* 对象中的各字段也会被合并到组件的 props 中。
*/
const mapDispatchToProps = dispatch => ({
plus: () => dispatch(incrementActionCreator()),
minus: () => dispatch(decrementActionCreator()),
reset: () => dispatch(resetCreator()),
})
/* 调用 connect() 函数,在 React 组件中,连接 redux 的 store */
const hoc = connect(mapStateToProps, mapDispatchToProps)
export default hoc(App)
actions/counter.js
// export const INCREMENT = 'increment'
// export const DECREMENT = 'decrement
// import { DECREMENT, INCREMENT } from "./constants"
/**
* action creator,action创建函数,主要用于生成 action 对象
* @returns
*/
export const incrementActionCreator = () => ({
// type: INCREMENT,
type: "increment",
payload: {
num: 5
},
})
export const decrementActionCreator = () => ({
type: "decrement",
payload: {
num: 2
},
})
export const resetCreator = () => ({
type: "RESETACTION",
payload: {
num: 0
},
})
store/index.js
import { createStore } from 'redux'
import rootReducer from '../reducers'
// 创建 Store 对象
export default createStore(rootReducer)
reducers/counter.js
import { DECREMENT, INCREMENT } from "../actions/constants"
/**
* state 初始值
*/
const initialState = {
count: 32, // 计数数量的状态数据
num:4
}
/**
* reducer 纯函数,用于实现状态的同步更新,返回更新后的新状态数据
* @param {*} state 更新前的状态数据
* @param {*} action 描述发生了什么,有 type 与 payload 属性
* @returns
*/
const reducer = (state = initialState, { type, payload }) => {
switch (type) {
case INCREMENT: // 加数量
const copy = { ...state } // 复制 state 值
copy.count += payload.num // 对复制后的副本进行更新
return copy // 返回更新后的新状态数据
case DECREMENT: // 减数量
const cp = { ...state } // 复制 state 值
cp.count -= payload.num // 对复制后的副本进行更新
return cp // 返回更新后的新状态数据
case "RESETACTION": // 减数量
const test1 = { ...state } // 复制 state 值
test1.count = payload.num // 对复制后的副本进行更新
return test1 // 返回更新后的新状态数据
default:
return state
}
}
export default reducer
reducers/index.js
import { combineReducers } from 'redux'
import counter from './counter'
// 将多个独立的 reducer 最终合并为一个根 reducer
export default combineReducers({
counter
})
CSS 框架 Bulma
在项目入口index.js
import 'bulma/css/bulma.min.css'
解决 react is defined but never used no-unused-vars
根目录创建.eslintrc.json
{
"plugins": ["react"],
"extends": [
"plugin:react/recommended"
]
}
render
类组件渲染 App.JSX
import React, { Component } from 'react'
import './App.css';
import withFooter from './utils/width-footer'
class App extends Component {
render(){
return (
<h1>Hello, world! </h1>
)
}
}
// export default withFooter(App)
export default App
创建版本包裹组件 将App 当参数传递给withFooter
import withFooter from './utils/width-footer'
export default withFooter(App)
Component
zh-hans.reactjs.org/docs/react-…
如需定义 class 组件,需要继承 React.Component:
React.Component 的子类中有个必须定义的 render() 函数。
使用 Component 创建width-footer.js
import React from 'react'
/**
* HOC
* @param {*} InComponent
* @returns
*/
const widthfooter = InComponent =>{
console.log(InComponent,'接收App');
class OutComponent extends React.Component{
render(){
return (
<>
<InComponent />
<div>版权声明© 1</div>
</>
)
}
}
return OutComponent
}
export default widthfooter
ES6 Array.fill( )
来填充一个全是空白对象的数组
state = {
todos: Array(6).fill(null).map((v, i) => ({
id: i + 1,
title: '待办事项' + (i + 1),
completed: true
}))
// todos: []
}
createContext
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。
import { Provider } from './utils/TodoContext'
引入共享工具 包裹组件最外层 被抱被包裹的组件 被包裹的组件同样需要引入共享工具TodoContext.js
使用的地方 static contextType = TodoContext
console.log(this.context,"this.context"); // name:测试
<Provider value={{name:'测试'}}/>
import React from 'react'
// 创建 Context 对象
const TodoContext = React.createContext()
// 解构生产与消费组件
const {Provider, Consumer} = TodoContext
// 导出
export {
TodoContext,
Provider,
Consumer,
}
在需要使用的页面 先引入/utils/TodoContext
prop-types
prop-types检测数据类型 创建头部header
import TodoHeader from './components/TodoHeader'
class App extends Component {
state = {
todos: Array(6).fill(null).map((v, i) => ({
id: i + 1,
title: '待办事项' + (i + 1),
completed: true
}))
// todos: []
}
render(){
return (
<Provider value={{name:'测试'}}>
<TodoHeader title="待办事项列表" subtitle="ToDoList..."></TodoHeader>
</Provider>
)
}
}
TodoHeader.jsx
import React, { Component } from 'react'
// 定义类型
import PropTypes from 'prop-types'
export default class TodoHeader extends Component {
// 设置运行时类型检测规则
static propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
children: PropTypes.string,
}
// 设置组件接收属性的默认值
static defaultProps = {
title: '默认的主标题',
subtitle: '默认的副标题',
children:""
}
render (){
const { title, subtitle, children } = this.props
return (
<section className="hero is-primary">
<div className="hero-body">
{
children
?
children
:
(
<>
<p className="title">
{ title }
</p>
<p className="subtitle">
{ subtitle }
</p>
</>
)
}
</div>
</section>
)
}
}
setState
react如果需要修改state的值需要使用setState
state = {
type: 'all' // 选项卡状态
}
// 点击,设置当前选项卡状态
handleChange(type, event) {
event.preventDefault()
this.setState({
type
})
}
react渲染模式模式显示在页面上
render()
{
(
<div>
{
this.state.todos.map(item => (
<div key={item.id}>
{item.title}
</div>
))
}
</div>
)
}
组件通讯(函数组件 类组件)
父传子
类组件
<TodoHeader title="待办事项列表" subtitle="ToDoList..."></TodoHeader>
const { title, subtitle, children } = this.props 子组件
// 父组件
<Hello name="jack" age={19} />
// 子组件
class Hello extends React.Component {
render() {
return (
<div>接收到的数据:{this.props.age}</div>
)
}
}
函数组件
// 父组件
<Hello name="jack" age={19} />
// 子组件
function Hello(props) {
console.log(props)
return (
<div>接收到数据:{props.name}</div>
)
}
子传父
// 父组件提供函数并且传递给字符串
class Parent extends React.Component {
getChildMsg = (msg) => {
console.log('接收到子组件数据', msg)
}
render() {
return (
<div>
子组件:<Child getMsg={this.getChildMsg} />
</div>
)
}
}
// 子组件接收函数并且调用
class Child extends React.Component {
state = { childMsg: 'React' }
handleClick = () => {
this.props.getMsg(this.state.childMsg)
}
return (
<button onClick={this.handleClick}>点我,给父组件传递数据</button>
)
}
跨级组件
跨级组件:所谓跨级组件通信,就是父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:
中间组件层层传递 props 使用 context 对象 对于第一种方式,如果父组件结构较深,那么中间的每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是这些中间组件自己所需要的。不过这种方式也是可行的,当组件层次在三层以内可以采用这种方式,当组件嵌套过深时,采用这种方式就需要斟酌了。
使用 context 是另一种可行的方式,context 相当于一个全局变量,是一个大容器,我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。 使用 context 也很简单,需要满足两个条件:
上级组件要声明自己支持 context,并提供一个函数来返回相应的 context 对象 子组件要声明自己需要使用 context
进入正题--------------- 先来看下不跨层级传递参数:
import React, { Component } from 'react'
class Grandson extends Component {
render() {
return (
<header>
// 孙子组件接收来自子组件的参数
{ this.props.title }
</header>
)
}
}
class Child extends Component {
render() {
return (
{/* 子组件先拿到父组件的参数
然后 传递给孙子组件 */}
<Grandson title={ this.props.title } />
)
}
}
class App5 extends Component {
render() {
return (
{/* 父组件的参数 title
通过Child组件 传递给子组件*/}
<Child title='标题' />
)
}
}
export default App5
再来看看context跨层级传递参数:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Grandson extends Component {
// 3, 设置参数类型
static contextTypes = {
title: PropTypes.string
}
render() {
return (
<header>
{/* 这里说明,携带参数的,已经不是props了,
而是context */}
{ this.context.title }
</header>
)
}
}
class Child extends Component {
render() {
return (
<Grandson />
)
}
}
class App5 extends Component {
// 2,设置参数类型 写法固定
static childContextTypes = {
title: PropTypes.string
}
// 1,返回一个对象,里面是携带的参数。固定写法
getChildContext() {
return {
title: '标题'
}
}
render() {
return (
<Child />
)
}
}
export default App5
createRef
import React, { Component, createRef } from 'react'
// 创建 Ref 对象
inputRef = createRef()
// 文本框自动获得焦点
// 字符串 ref 已废弃,不建议再使用
// this.refs.inputRef.focus()
// console.log(this.inputRef)
this.inputRef.current.focus()
Route
v5.reactrouter.com/web/api/Rou… 路由 在项目入口index.js 引入 react-router-dom 并且包裹app
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom'
import App from './App'
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
)
在App.jsx中同样引入
import React from 'react'
import { Route, Link, Redirect, Switch } from 'react-router-dom'
import TabBar from './components/TabBar'
import Category from './views/Category'
import Home from './views/Home'
import Mine from './views/Mine'
export default function App() {
return (
<div>
App
<ul>
<li><Link to="/home">首页</Link></li>
<li><Link to="/category">分类</Link></li>
<li><Link to="/mine">我的</Link></li>
</ul>
<Switch>
<Route path="/category" component={Category} />
<Route path="/home">
<Home />
<TabBar />
</Route>
<Route path="/mine" render={() => (
<>
<Mine />
<TabBar />
</>
)} />
<Redirect to="/home" />
</Switch>
</div>
)
}
withRouter
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
class SubCategory extends Component {
render() {
console.log('sub:', this.props)
return (
<div>
子分类页面 { this.props.location.search || JSON.stringify(this.props.match.params) }
<button onClick={() => this.props.history.push('/home')}>首页</button>
</div>
)
}
}
// 如果在组件中需要使用到路由的 history、location、match 对象,但
// 组件属性中这些对象不存在,则可以使用 withRouter() 这个 HOC 来为
// 包裹组件注入(增强)这几个对象的使用
// export default SubCategory
export default withRouter(SubCategory)
import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import SubCategory from './SubCategory'
export default class Category extends Component {
render() {
return (
<div>
分类页面-主分类
<div><Link to="/category/sub?id=11111111">分类1</Link></div>
<div><Link to="/category/sub/2222222">分类2</Link></div>
<Route path="/category/sub/:id?">
<SubCategory />
</Route>
</div>
)
}
}