文章目录
创建
脚手架搭建
npx create-react-app 项目名
创建组件
导入组件
import React from "react"
import ReactDOM from 'react-dom'
render
导入视图
1.使用箭头函数
-
基本
//方法 const creactApp =(props)=>{ return ( <div> {/* 只要在jsx中里擦呼入js的代码,就加一层花括号,注释也是js,所以这里的注释加了一层花括号 */} <h1>Welcome {props.title}!!!!</h1> <p>youxiu</p> </div> ) } //实现 const app = creactApp({ title:"React 16.8", })x ReactDOM.render( app, document.querySelector("#root") )
-
化简
const App =(props)=>{ return ( <div> {/* 只要在jsx中里擦呼入js的代码,就加一层花括号,注释也是js,所以这里的注释加了一层花括号 */} <h1>Welcome {props.title}!!!!</h1> <p>youxiu</p> </div> ) } ReactDOM.render( <App title="1901"/>, document.querySelector("#root") )
2.使用类组件
-
方法
const Header = () => <h1>类组件!!!</h1> class App extends React.Component{ render () { console.log(this.props) return ( <div> <Header /> <h1>类组件</h1> <p>{this.props.title},{this.props.x}</p> </div> ) } } render(x <App title="类组件继承" x="abc"/>, document.querySelector('#root') )
-
render原理
const app = new App({ title:'类组件继承' }).render() render( app, document.querySelector('#root') ) render( <App title="类组件继承" x="abc"/>, document.querySelector('#root') )
使用样式
const Title = styled.h1`color:#F00`
class App extends Component{
render(){
const style = {color:"#f00"}
return(
<div>
{/* <h1 style={{color:"red"}}>元素中的样式</h1> */}
<Title >元素中的样式</Title>
<ol>
<li style = {style}>使用style的link样式创建</li>
<li className="has-text-red">使class的方式,但是在react里class要写成className</li>
<li className={classNames('a',{'b':true,"c":false})}>
要动态添加不同的classname就可以使用第三方的包叫className,比如这个里标签上只有啊,b没有c
</li>
</ol>
<li >style-components</li>
</div>
)
}
}
开始搭建平台
-
启动器
index.js
import React from 'react' import {render} from 'react-dom' import App from './App' render( <App />, document.querySelector("#root") )
-
框架
rcc
类模式,rfc
函数模式app.js(以类模式为例)
import React, { Component,Fragment } from 'react' import { TodoHeader, TodoInput, TodoList } from './components'//各种组件 export default class App extends Component { render() { return ( <Fragment> app <TodoHeader /> <TodoInput /> <TodoList /> </Fragment> ) } }
因为导出时只能返回一个根标签所以要加div但是要是显示时想去掉:
1.导入react的
Fragment
写成上述方式2.直接写成<></>
变量
导入变量
app.js
<TodoHeader desc="今日事,今日毕">
<i>代办事项</i>{/*props.children变量*/}
</TodoHeader>
组件
- 函数形式
export default function TodoHeader(props) {
console.log(props)
return (
<div>
<h1>
{props.children}
</h1>
<h3>{props.desc}</h3>
</div>
)
}
-
类模式
export default class TodoList extends Component { render() { return ( <div> TodoList <TodoIitem /> </div> ) }
}
```
-
外部组件(类)
export default class App extends Component { constructor(){ super() this.state = { desc: "今日事,今日毕", title:"sjkfa" } } render() { return ( <Fragment> <TodoHeader desc={this.state.desc}></TodoHeader> </Fragment> ) } }
注意:
1.数字变量要用于运算要写成
{2}
管理变量
install
npm install --save prop-types
函数模式
export default function TodoHeader(props) {...}
//组件的样式
TodoHeader.propTypes={
desc:propTypes.string.isRequired,
x:propTypes.number,
y:propTypes.number,
children:propTypes.object
}
//组件的默认值
TodoHeader.defaultProps = {
desc:"如果还有明天"
}
类模式
export default class TodoInput extends Component {
//组件的样式
static propTypes = {
btnText: PropTypes.string
}
//组件的默认值(变量名不可变)
static defaultProps = {
btnText: "添加ADD"
}
render() {...}
}
管理类的方式
export default class TodoList extends Component {
static propTypes = {
todos:PropTypes.arrayOf(PropTypes.shape({
id:PropTypes.number.isRequired,
title: PropTypes.string.isRequired
}))
}
}
字符转html
<div dangerouslySetInnerHTML={{__html: this.state.article}}></div>
循环
this.state.todos.map(todo =>{
return <div key={todo.id}>{todo.title}</div>
})
方法
使用onClick
方法来监听事件的发生
在类中编译方法
使用this.state.isLiked != this.state.isLiked
可以改值但是界面不会重新渲染
应该使用this.setState
方法来渲染界面
界面:
export default class Like extends Component {
constructor(){
super()
this.state = {
isliked: false
}
}
handleLikedClick = ()=>{
//使用这种方式可以改值但是界面不会重新渲染
//this.state.isLiked != this.state.isLiked
//要修改数据,就使用setState方法,setState有两个参数
//第一个参数又有两种:
//对象形式:
// this.setState({
// isliked: !this.state.isliked
// })
//第二是方法,
this.setState((prebState,props)=>{
console.log("like的prebState",prebState,props)
console.log("setStaate内部的this.state.isliked",this.state.isliked)
return {
isLiked:! prebState.isLiked//改值是在返回时发生的
}
},()=>{
//由于setState是一个异步方法,如果想要最新的state,应该在这个回调里来获取
console.log(this.state.isLiked)
})
console.log("setStaate外部的this.state.isliked",this.state.isliked)
}
render() {
return (
<div>
<span onClick={this.handleLikedClick}>
{
this.state.isliked ?'取消❤️':'喜欢 🖤'
}
</span>
</div>
)
}
}
输入框改值
bind只有值发生变化时才会用
handleInputChange = (e)=>{
// console.log(e.currentTarget.value)
this.setState({
inputValue:e.currentTarget.value
})
}
为了让函数只渲染一次就把函数的赋值在构造中
constructor(){
super()
this.state={
inputValue:"ert"
}
this.handleAddClickCopy = this.handleAddClick.bind(this)
}
handleAddClick() {
console.log(this.state)
}
render() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange}/>
<button onClick={this.handleAddClick}>{this.props.btnText}</button>
</div>
)
}
优化
修改前:
<button onClick={()=>{
console.log(this.state)
}}>{this.props.btnText}</button>
因为这样做会渲染方法所以我们将函数的定义放在外面
-
渲染时将方法修改
<button onClick={()=>{this.handleAddClick(1234)}}>{this.props.btnText}</button>
渲染时将方法执行
<button onClick={this.handleAddClick(1234)}>{this.props.btnText}</button>
为了方便(综合上面的优势)得
<button onClick={this.handleAddClick.bind(this,1234)}>{this.props.btnText}</button>
-
全局模式的定义
addTodo = (todoTitle)=>{ console.log(todoTitle) } render() { return ( <Fragment> <TodoInput addTodo={this.addTodo}/> <TodoList todos={this.state.todos}/> </Fragment> ) }
TodoInput组件:
handleAddClick = ()=>{ this.props.addTodo(this.state.inputValue) } render() { return ( <div> <input type="text" value={this.state.inputValue} onChange={this.handleInputChange}/> <button onClick={this.handleAddClick}>{this.props.btnText}</button> </div> ) }
修改值(界面更新使用)
同样我们要使用setState
方法就可以进行渲染(全局的!!)
addTodo = (todoTitle) => {
console.log(todoTitle)
// this.setState({
// 返回了长度 todos =3了
// todos:this.state.todos.push({
// id:Math.random(),
// title:todoTitle,
// isCompleted:false
// })
// })
// //正确
// this.setState({
// todos: this.state.todos.concat({
// id: Math.random(),
// title: todoTitle,
// isCompleted: false
// })
// })
// const newTodos = this.state.todos.slice()
const newTodos = [...this.state.todos]
newTodos.push({
id: Math.random(),
title: todoTitle,
isCompleted: false
})
this.setState({
todos:newTodos
})
}
多级方法的优化
app.js
<TodoList todos={this.state.todos}
onCompeletedChange={this.onCompeletedChange} />
<TodoIitem
onCompeletedChange={this.props.onCompeletedChange}
key={todo.id}
{...todo}
/>
const noop=() =>{}
export default class TodoItem extends Component {
handleCheckboxChange = ()=>{
console.log("change")
//如果最后没有传过来可以继续运行
// this.props.onCompeletedChange&&this.props.onCompeletedChange(this.props.id)
const {
onCompeletedChange = noop,
id
}=this.props
onCompeletedChange(id)
}
render() {
// console.log("TodoItem",this.props)
const {
isCompleted,
title
} = this.props
return (
<li>
<input type="checkbox"
checked={this.props.isCompleted}
onChange={this.handleCheckboxChange}
/><span>{this.props.title}:{this.props.isCompleted?"已完成":"未完成"}</span>
</li>
)
}
}
针对多次渲染的问题
1.使用react的PureComponent类代替Component
export default class TodoItem extends PureComponent {
constructor (){
super()
this.state = {}
}
handleCheckboxChange = ()=>{
const {
onCompeletedChange = noop,
id
}=this.props
onCompeletedChange(id)
}
render() {
console.log(`TodoItem ${this.props.title}render`)
// console.log("TodoItem",this.props)
const {
isCompleted,
title
} = this.props
return (
<li>
<input type="checkbox"
checked={this.props.isCompleted}
onChange={this.handleCheckboxChange}
/><span>{this.props.title}:{this.props.isCompleted?"已完成":"未完成"}</span>
</li>
)
}
}
跳转
react里面通过ref来获取组件或者dom元素,要使用ref之前必须先调用
React.createRef
方法来创建一个ref
生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/659d67e5eccbb725246cbb2f18deeb81.png)
UNSAFE_componentWillMount
![](https://i-blog.csdnimg.cn/blog_migrate/e5feda8b16808938cf3da6a3659e6386.png)
static getDerivedStateFromProps(){
return {}
}
//上面那个函数有了这个了,所以下面这个不能存在
//这个函数在更新前启动
/*
UNSAFE_componentWillMount(){
console.log('UNSAFE_componentWillMount')
}*/
更新
- 老版本的坑:
constructor
里面通过props
来初始化一个state,在props修改之后,这个state不会再次更新
constructor (props){
super()
this.state = {
competedText:nextProps.isCompleted?'完成':'未完成'
}
}
那么就需要借助于UNSAFE_componentWillReceiveProps
来做一次修正
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
competedText:nextProps.isCompleted?'完成':'未完成'
})
}
- 新方法
constructor(){
super()
this.state={
competedText:''
}
}
static getDerivedStateFromProps (props){
return{
competedText:props.isCompleted?'完成':'未完成'
}
}
网络编程(axios)
npm install axios -S
Hook
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
import React, { useState } from 'react';
function Example() {
// 声明一个新的叫做 “count” 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
监视更新
useEffect((=>{
document.title="标题"
}))
例:
//两个常见的api就是 useState,useEffect,需要先引入
import React, { useState,useEffect } from 'react'
import {render} from 'react-dom'
const Counter = () =>{
// useState是一个方法,这个方法的参数就是默认值,结果是一个数组,数组的第一个就是state,第二个就是相当于setSate
//解构出来两个数组中的两个值
const [count, setcount] = useState(0)
//userEffer就是一个回调,不管是组件的挂载还是更新,都会触发这个回调方法
useEffect(()=>{
console.log("渲染了")
document.title = `当前数量为${count}`
})
return (
<div>
<p>当前数量为{count}</p>
<button onClick={()=>{setcount(count-1)}}>-</button>
{/*这里的setCount就是useState所生成的方法。注意和setState的参数不同,这里的参数是一个新值即可*/}
<span>{count}</span>
{/* 这里就是useState创建的值 */}
<button onClick={()=>{setcount(count+1)}}>+</button>
</div>
)
}
render(
<Counter/>,
document.querySelector("#root")
)
Context
//createContext是react提供的一个用于跨组件创制的方法
import React, { Component, createContext } from 'react'
import { render } from "react-dom"
console.log(createContext())
//createContext这个方法的结果是一个对象,里面有Provider和Consumer
//Provider用于提供状态
//Consumer用于接收状态
const {
Provider,
Consumer: CounterConsumer//结构出来重新赋值给一个CounterConsumer的组件
} = createContext()
//封装一个基本的Provider,因额外i之间使用Provider,不方便管理状态
class CounterProvier extends Component {
constructor() {
super()
//这里的组
this.state = {
count: 100
}
}
incrementCount = () => {
this.setState({
count: this.state.count + 1
})
}
decrementCount = () => {
this.setState({
count: this.state.count - 1
})
}
render() {
return (
//使用Provider这个组件,它必须要有一个value值,这个value里可以传递任何的数据,一般还是传送一个对象比较合理
<Provider value={{
count: this.state.count,
onDecrementCount: this.decrementCount,
onIncrementCount: this.incrementCount
}}>
{this.props.children}
</Provider>
)
}
}
//定义一个Counter组件
class Counter extends Component {
// state
render() {
return (
//使用Count了Cousumer来接收count
<CounterConsumer>
{
//注意注意!!!Coumer的children必须是一个方法,这个方法有一个参数,这个参数就是Provider的value
({ count }) => {
//
// console.log(count)
return <span>{count}</span>
}
}
</CounterConsumer>
)
}
}
class CountBtn extends Component {
render() {
return (
<CounterConsumer>
{
({ onDecrementCount, onIncrementCount }) => {
const handler = this.props.type === 'increment' ? onIncrementCount : onDecrementCount
return <button onClick={handler}>{this.props.children}</button>
}
}
</CounterConsumer>
)
}
}
class App extends Component {
render() {
return (
<>
<CountBtn type="decrement">-</CountBtn>
<Counter />
<CountBtn type="increment">+</CountBtn>
</>
)
}
}
render(
<CounterProvier>
<App />
</CounterProvier>,
document.querySelector('#root')
)
HOC及CRA
![高阶函数](..\image\高阶函数.png)
高阶组件
react-lesson-03
通过使用参数是函数的方法来装饰组件
-
整体表示
const testHOc = (WrappedComponent) =>{ return class HoCComponent extends Component{ render() { return ( <> <WrappedComponent/> <div>这是高阶组件里的信息</div> </> ) } } } class App extends Component { render() { return ( <div> <Button type="primary">dian</Button> App </div> ) } } export default testHOc(App)
-
分开表示
index.js
import React from 'react'
import {render} from 'react-dom'
import App from './App'
render(
<App />,
document.querySelector('#root')
)
App.js
import React,{Component} from 'react'
import withCopyright from './withCopyright'
import Another from './Another'
class App extends Component {
render(){
return (
<div>
App
<Another name="组件"/>
</div>
)
}
}
export default App
withCopyright.js
import React, { Component } from 'react'
const withCopyright = (YourComponent) =>{
return class withCopyright extends Component {
render () {
console.log(this)
return (
<p>
<YourComponent {...this.props}/>
这是高阶内容
<div>©2019   前锋教育</div>
<p/>
)
}
}
}
export default withCopyright
Another.js
import React, { Component } from 'react'
import withCopyright from './withCopyright'
class Another extends Component {
render() {
return (
<div>
AnOther {this.props.name}
</div>
)
}
}
export default withCopyright(Another)
装饰器模式
Another.js变成了这样(写成这样)
import React, { Component } from 'react'
import withCopyright from './withCopyright'
@withCopyright
class Another extends Component {
render() {
return (
<div>
AnOther {this.props.name}
</div>
)
}
}
export default Another
方法
npm install react-app-rewired --save-dev
然后将package.json的build,test,start运行编码的react-scripts
改为react-app-rewired
npm install customize-cra --save-dev
npm i @babel/plugin-proposal-decorators -D
让cra支持@装饰器写法:
-
不管你是要配置什么,我们最好的方式是使用
react-app-rewired
这个包来对cra
创建的项目进行轻微的配置调整 -
安装好之后,在
package.json
里把scripts
里的react-scripts
替换成reactapp-rewired
-
在根目录下创建一个
config.overrides.js
const { override,addDecoratorsLegacy } = require('customize-cra') module.exports = (config) => { //如果没有customize-cra在这里可以对config配置 return config }
-
当然如果想要配置更方便可以安装
customize-cra
然后config.overrides
为const { override,addDecoratorsLegacy } = require('customize-cra') module.exports = override( addDecoratorsLegacy() )
状态管理:
**https://git.dev.tencent.com/leochow/react-tuts-1901.git
Redux
HTMl样式
**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<button onclick="store.dispatch({type:'JIAN',n:2})">-</button>
<span id="countDisplay"></span>
<button onclick="store.dispatch({type:'JIA',n:2})">+</button>
</div>
<script>
const countDisplay = document.querySelector('#countDisplay')
const countState = {
count: 10
}
// const renderCount = (state)=>{
// countDisplay.innerHTML = state.count
// }
// renderCount(countState)
const changeState = (state, action) => {
if(!state) {
return countState
}
switch (action.type) {
case 'JIA':
return {
...state,
count:state.count+action.n
}
case 'JIAN':
return {
...state,
count:state.count-action.n
}
default:
return state
}
}
const dispatch = (action) => {
changeState(action)
renderCount(countState)
}
const createStore = (render) => {
let state=null
const getState = () => state
const listeners = []
const subsribe = (listener) => listeners.push(listener)
const dispatch = (action) => {
state = changeState(state,action)
listeners.forEach(listener => listener())
}
dispatch({})
return {
getState,
dispatch,
subsribe
}
}
const store = createStore( changeState)
const renderCount = () => {
countDisplay.innerHTML = store.getState().count
}
renderCount()
store.subsribe(renderCount)
</script>
</body>
</html>
手写
lesson-04
npm i redux -S
1.编写一个store.js文件
import { createStore } from 'redux'
import rootReducer from './reducers'
export default createStore(rootReducer)
react-redux
lesson-05
npm install --save react-redux
dispatch()
运行一次redux方法
<tbody>
{
// console.log(id)
this.props.cartList.map(item => {
return (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.price}</td>
<td>
<button onClick={
() => {
this.props.reduce(item.id)
}
}>-</button>
<span>{item.amount}</span>
<button onClick={
() => {
this.props.add(item.id)
}
}>+</button>
</td>
</tr>
)
})
}
</tbody>
// mapStateToProps, 这里的state实际上就store.getState()的值
const mapState = (state) => {
// 这里return了什么,在组件里就可以通过this.props来获取
return {
cartList: state.cart
}
}
const mapDispatchToProps = dispatch =>{
return{
add:(id)=> dispatch(increment(id)),
reduce: (id) =>dispatch(decrement(id))
}
}
export default connect(mapState,mapDispatchToProps)(CartList)
目前编写方法
<button onClick={this.props.decrement.bind(this,item.id)}>-</button>
const mapDispatchToProps = dispatch =>{
return{
add:(id)=> dispatch(increment(id)),
reduce: (id) =>dispatch(decrement(id))
}
}
export default connect(mapState,{increment,decrement,decrementAsyn})(CartList)
1.创建reducers
2.合并reducers
3.createStore
4.Provider store = {store}
5.connect (mapStateToProps,{。。actionCon})
React-Router
react-lesson-08
npm install react-router-dom -S
渲染方式
在App.js文件中加入router-dom组件的HashRouter
和Route
import React from 'react'
import { render } from 'react-dom'
// import { BrowserRouter as Router,Route } from 'react-router-dom'
import {HashRouter as Router,Route} from 'react-router-dom'
import App from './App'
render(
<Router >
<Route component={App}/>
</Router>,
document.querySelector("#root")
)
组件
-
route
路由器组件component
= {组件}path
= ‘\path’
-
Link
路由器跳转 -
to
跳转路径 -
HashLink
功能比Link
多一个Classname
属性 -
Redirect
从一个路径转换到另一个路径from
原始路径to
跳转路径
-
Switch
选择路径,使得渲染成功后不再继续搜素下一个路由<Switch> <Route component={Home} path="/home"></Route> <Route component={Artical} path="/artical" exact/> <Route component={ArticalDetail} path="/artical/:id"/> <Route component={Users} path="/user" /> <Redirect to="/home" from="/"/> </Switch>
exact
用于继续运行
附加:
render
方式组件
<Route path="/home" render={(routerProps)=>{
console.log(routerProps)
return<Home {...routerProps} x={1}/>
}}></Route>
router的页面跳转(的跳转方式)只有使用router组件时才可以使用router的api
goHome = () => {
// this.props.history.push('/home')
this.props.history.push({
pathname: '/home',
state: {
id: this.props.match.params.id
}
})
}
在非router组件的router中需要添加RouterwirhRouter
组件
import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
class A exend Component{}
export default withRouter(A)
传参方式:
-
query
-
使用
state
隐式传参<Link to={{ pathname:"/artical/2", state:{ from:'qed'//隐式传参 } }}>文章二</Link>
-
动态路由
/path/:param
=>params<Link to="/artical/1/hhh"/> {/*route.path="//artical/:id/:from" */}
Ant Design(软件的使用)
npm i antd -S
高级配置
npm i react-app-rewired customize-cra -D
npm i babel-plugin-import -D
配置config-overrides.js
文件在根目录
const { override, fixBabelImports } = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',//手机端是antd-mobile
libraryDirectory: 'es',
style: 'css',
}),
);
设置自定义
npm i less less-loader -D
const { override, fixBabelImports ,addLessLoader} = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',//手机端是antd-mobile
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
javascriptEnabled: true,
modifyVars: {
'@primary-color': '#F00' //设置颜色
}
})
);
国际化
import React from 'react';
import {render} from 'react-dom'
import App from './App';
import zhCN from 'antd/lib/locale-provider/zh_CN'
import {LocaleProvider} from 'antd'
render(
<LocaleProvider locale={zhCN} >
<App/>
</LocaleProvider>,
document.getElementById('root'));
项目
标准的注释语法
/**
* @file config-overrides.js
* @author ChuyongWei
* 基于customize-cra和react-app-rewired的指定化配置文件
*/
懒加载
npm i react-loadable -S
原理:
// 这个文件只是用于解释react-loadable原理用。当然可以无缝切换
import React, { Component } from 'react'
const Loadable = ({
loader,
loading: Loading
}) => {
return class LoadableComponent extends Component {
state = {
LoadedComponent: null
}
componentDidMount() {
// import('./Dashboard')
loader()
.then(resp => {
this.setState({
LoadedComponent: resp.default
})
})
}
render() {
const {
LoadedComponent
} = this.state
return (
LoadedComponent
?
<LoadedComponent />
:
<Loading />
)
}
}
}
export default Loadable
使用
类似于导出
import Loadable from 'react-loadable'
import { Loading } from '../components'//等待时的页面
const ArticleEdit = Loadable({
loader: () => import('./Article/Edit'),//引用的页面
loading: Loading
})
export {Dashboard}
如果遇到有些组件导不进去就从替他地方导