2019年最新 React从入门到实战(带 React 企业级实战项目-宜居)
(React Hook新特性&)
React Hook新特性&
(1.React 新特性 State Hook&)
1.React 新特性 State Hook&
App.js
import React from 'react';
import Demo1 from "./components/Demo1"
import Demo2 from "./components/Demo2"
import User from "./components/Demo3/User"
import Token from "./components/Demo3/Token"
import TodoList from "./components/Demo4/TodoList"
import Demo5 from "./components/Demo5"
import Demo6 from "./components/Demo6"
import Demo7 from "./components/Demo7"
import Demo8 from "./components/Demo8"
import Demo9 from "./components/Demo9"
import Demo10 from "./components/Demo10/MemoDemo"
import Demo11 from "./components/Demo11"
import Demo12 from "./components/Demo12"
import Demo13 from "./components/Demo13/MainPage"
import Demo14 from "./components/Demo14"
import Demo15 from "./components/Demo15"
function App() {
return (
<div className="App">
{/* <Demo1 /> */}
{/* <Demo2 /> */}
{/* <User /> */}
{/* <Token /> */}
{/* <TodoList /> */}
{/* <Demo5 /> */}
{/* <Demo6 /> */}
{/* <Demo7 /> */}
{/* <Demo8 /> */}
{/* <Demo9 /> */}
{/* <Demo10 /> */}
{/* <Demo11 /> */}
{/* <Demo12 /> */}
{/* <Demo13 /> */}
{/* <Demo14 /> */}
<Demo15 />
</div>
);
}
export default App;
Demo1.jsx
Hook创建component,useState
import React,{ useState } from "react"
// export default class Demo1 extends React.Component{
// state = {
// count:0
// }
// render(){
// return(
// <div>
// Hello:{ this.state.count }
// </div>
// )
// }
// }
export default () =>{
/**
* count:状态
* setCount:setState -> setCount修改状态
* useState(0):默认值
*/
const [count,setCount] = useState(10);
const [page,setPage] = useState(0);
return(
<div>
Hello:{ count } - { page }
<button onClick={ () =>{ setCount(count+1) } }>Add</button>
</div>
)
}
(2.React 新特性 Effect Hook&)
2.React 新特性 Effect Hook&
Demo2.jsx
useEffect,componentDidUpdate
// import React from "react"
// export default class Demo2 extends React.Component{
// state = {
// count:0
// }
// componentDidMount(){
// document.title = `You clicked ${this.state.count} times`
// }
// componentDidUpdate(){
// document.title = `You clicked ${this.state.count} times`
// }
// render(){
// return(
// <div>
// Hello:{ `You clicked ${this.state.count} times` }
// <button onClick={ () =>{ this.setState({count:this.state.count+=1}) } }>Add</button>
// </div>
// )
// }
// }
import React,{ useState,useEffect } from "react"
export default () =>{
const [count,setCount] = useState(0);
/**
* useEffect:
* componentDidMount
* componentDidUpdate
* componentWillUnmount
*/
useEffect(() =>{
document.title = `You clicked ${count} times`
})
return(
<div>
Hello:{`You clicked ${ count } times`}
<button onClick={ () =>{ setCount(count+1) } }>Add</button>
</div>
)
}
(3.新特性 State Hook 和 Effect Hook实例应用&)
3.新特性 State Hook 和 Effect Hook实例应用&
User.jsx useState代替事件
// import React from "react"
// const userSet = ["张三","李四","王五","赵刘"]
// export default class UserGenerator extends React.Component{
// state = {
// user:userSet[0]
// }
// generateUser = () =>{
// let randomIndex = Math.floor(Math.random() * userSet.length);
// let randomUser = userSet[randomIndex]
// this.setState({
// user:randomUser
// })
// }
// render(){
// return(
// <div>
// <span>{ this.state.user }</span>
// <button onClick={ this.generateUser }>切换</button>
// </div>
// )
// }
// }
import React, { useState } from "react"
const userSet = ["张三", "李四", "王五", "赵刘"]
const UserGenerator = () => {
const [user, setUser] = useState(userSet[0])
const generateUser = () => {
let randomIndex = Math.floor(Math.random() * userSet.length);
let randomUser = userSet[randomIndex]
setUser(randomUser)
}
return (
<div>
<span>{user}</span>
<button onClick={generateUser}>切换</button>
</div>
)
}
export default UserGenerator
Token.jsx
sessionStorage读取token,ref非受控组件
import React,{useState,useEffect } from "react"
class TokenForm extends React.Component{
handlerSubmit = event =>{
event.preventDefault();
// 数据来源于props
const { setToken } = this.props;
const token = this.tokenInput.value;
if(token){
setToken(token)
}
}
render(){
return(
<form onSubmit={ this.handlerSubmit }>
<input type="text" name="token" placeholder="enter your token"
ref={ input => { this.tokenInput = input }}
/>
</form>
)
}
}
// export default class TokenApp extends React.Component{
// state = {
// token:null
// }
// componentDidMount(){
// // 数据存在本地
// this.setState({token:sessionStorage.getItem("token")});
// }
// setToken = token =>{
// sessionStorage.setItem("token",token);
// this.setState({token})
// }
// render(){
// const { token } = this.state;
// return(
// <div>
// <h1>Hello</h1>
// { token ? token : <TokenForm setToken={ this.setToken }/> }
// </div>
// )
// }
// }
const TokenApp = () =>{
const [token,setToken] = useState(sessionStorage.getItem("token"))
useEffect(() =>{
sessionStorage.setItem("token",token);
})
return(
<div>
<h1>Hello</h1>
{ token ? token : <TokenForm setToken={ setToken }/> }
</div>
)
}
export default TokenApp
(4.React 新特性 自定义 Hook TodoList&)
4.React 新特性 自定义 Hook TodoList&
TodoList.jsx 数组合并方法
import React,{useState} from "react"
import TodoForm from "./TodoForm"
const TodoList = () =>{
/**
* todolist是一个列表:数组
*/
const [todos,setTodos] = useState([]);
function setValue(text){
// 数组的合并
/**
* a = [1,2,3]
* b = [4,5,6]
* c = 7;
* [...a,...b,c]
*/
setTodos([{ text },...todos]);
}
return(
<div>
<TodoForm onSubmit={ setValue }/>
<div>
{
todos.map((element,index) =>{
return <div key={index}>{ element.text }</div>
})
}
</div>
<button onClick={ () =>{ setTodos([])} }>clear</button>
</div>
)
}
export default TodoList
TodoForm.jsx 三点读取数据
// import React,{ useState } from "react"
// const TodoForm =() =>{
// const [value,setValue] = useState("")
// return(
// <div>
// <input type="text" value={ value } onChange={ (e) =>setValue(e.target.value) }/>
// </div>
// )
// }
// export default TodoForm
// https://github.com/rehooks/awesome-react-hooks
import React, { useState } from "react";
const useInputValue = (initialValue) => {
const [value,setValue] = useState(initialValue);
return{
value,
onChange:e => setValue(e.target.value),
resetValue:() => setValue("")
}
}
const TodoForm =({ onSubmit }) =>{
const {resetValue , ...text} = useInputValue("");
function onSubmitHandler(e){
e.preventDefault();
onSubmit(text.value);
resetValue(); // 清空输入框
}
return(
<form onSubmit={ onSubmitHandler }>
<input type="text" { ...text }/>
</form>
)
}
export default TodoForm
创建hook输入框
(5.React 新特性 Hook Effect 性能优化&)
5.React 新特性 Hook Effect 性能优化&
componentDidUpddate优化
// import React from "react"
// export default class Demo5 extends React.Component{
// state = {
// count:0,
// name:"iwen"
// }
// componentDidMount(){
// document.title = `you clicked ${this.state.count} times`;
// }
// componentDidUpdate(prevProps,prevState){
// if(prevState.count !== this.state.count){
// console.log("触发");
// document.title = `you clicked ${this.state.count} times`;
// }
// }
// clickCountHandler = () =>{
// this.setState({
// count:this.state.count+1
// })
// }
// clickNameHandler = () =>{
// this.setState({
// name:"ime"
// })
// }
// render(){
// return(
// <div>
// <p>{ `you clicked ${this.state.count} times` }</p>
// <p>{ this.state.name }</p>
// <button onClick={ this.clickCountHandler }>click me</button>
// <button onClick={ this.clickNameHandler }>click me</button>
// </div>
// )
// }
// }
import React ,{ useState,useEffect } from "react"
const Demo5 = () =>{
const [count,setCount] = useState(0);
const [name,setName] = useState("iwen");
/**
* 第二个参数:
* []:相当于生命周期函数的:componentDidMount
* 没有第二个参数:相对于生命周期函数:componentDidMount componentDidUpdate
* [count]:只监听count发生改变的时候,才会触发 componentDidUpdate
*
* return:相当于componentWillUnmount
*/
useEffect(() => {
console.log("执行");
document.title = `you clicked ${count} times`;
},[count])
return(
<div>
<p>Your cliced { count } times</p>
<p>{ name }</p>
<button onClick={ () =>setCount(count+1) }>Click me</button>
<button onClick={ () =>setName("ime") }>Click me</button>
</div>
)
}
export default Demo5
(6.React 新特性 网络请求&)
6.React 新特性 网络请求&
Demo6.jsx async网络请求
import React,{ useState,useEffect } from "react"
const useFetch = (url) =>{
const [data,setData] = useState(null);
const [loading,setLoading] = useState(true);
useEffect(() =>{
(async () =>{
const response = await fetch(url);
const data = await response.json();
setData(data)
setLoading(false)
})();
})
return {data,loading}
}
const Demo6 = () =>{
// const [data,setData] = useState(null);
// const [loading,setLoading] = useState(true);
// useEffect(() =>{
// (async () =>{
// const response = await fetch("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php");
// const data = await response.json();
// setData(data)
// setLoading(false)
// })();
// })
const { data,loading } = useFetch("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php")
return(
<div>
{loading ? <div>...loading</div> : data.chengpinDetails[0].title }
</div>
)
}
export default Demo6
(7.React 新特性 React Hook useEffect 实例&)
7.React 新特性 React Hook useEffect 实例
Demo7.jsx get请求
// import React from "react";
// export default class GithubListClass extends React.Component {
// constructor(props) {
// super(props);
// this.state = {
// page: 1,
// commits: [
// ]
// };
// this.nextPage = this.nextPage.bind(this);
// this.firstPage = this.firstPage.bind(this);
// }
// nextPage() {
// this.setState({ page: this.state.page + 1 }, () => this.loadGithubCommits());
// }
// firstPage() {
// this.setState({ page: 1 }, () => this.loadGithubCommits());
// }
// loadGithubCommits() {
// const { page } = this.state;
// fetch(`https://api.github.com/search/commits?q=repo:facebook/react+css&page=${page}`, {
// method: "GET",
// headers: new Headers({ "Accept": "application/vnd.github.cloak-preview" }),
// })
// .then(data => data.json())
// .then(response => this.setState({ commits: response.items }))
// .catch(error => console.log(error))
// }
// componentDidMount() {
// this.loadGithubCommits();
// }
// render() {
// return (
// <div>
// {this.state.commits.length !== 0
// && <button onClick={this.nextPage}> next page </button>}
// {this.state.commits.length === 0
// && <button onClick={this.firstPage}> first Page </button>}
// {this.state.commits.map(c => (
// <div key={c.sha}>
// {
// c.commit && (
// <div className="commit-container">
// <p> {c.commit.committer.name}</p>
// <p> {c.commit.message}</p>
// </div>
// )
// }
// </div>
// ))}
// </div>
// );
// }
// }
import React, { useState, useEffect } from 'react'
const GithubListClass = () => {
const [page, setPage] = useState(1);
const [commits, setCommits] = useState([]);
const nextPage = () => {
setPage(page+1)
}
const firstPage = () => {
setPage(1)
}
useEffect(() => {
fetch(`https://api.github.com/search/commits?q=repo:facebook/react+css&page=${page}`, {
method: "GET",
headers: new Headers({ "Accept": "application/vnd.github.cloak-preview" }),
})
.then(data => data.json())
.then(response => setCommits(response.items))
.catch(error => console.log(error))
},[page]); // 一直执行
return (
<div>
{commits.length !== 0
&& <button onClick={nextPage}> next page </button>}
{commits.length === 0
&& <button onClick={firstPage}> first Page </button>}
{commits.map(c => (
<div key={c.sha}>
{
c.commit && (
<div className="commit-container">
<p> {c.commit.committer.name}</p>
<p> {c.commit.message}</p>
</div>
)
}
</div>
))}
</div>
)
}
export default GithubListClass
Demo8.jsx
Hook规则
import React,{ useState,useEffect } from "react"
// https://zh-hans.reactjs.org/docs/hooks-rules.html
const Demo8 = () =>{
const [count,setCount] = useState(0)
useEffect(() => {
if(count === 0){
document.title = `ou clicked ${ count } times`
}
})
return(
<div>
<p>You clicked { count } times</p>
<button onClick={ () => setCount(count+1) }>Click me</button>
</div>
)
}
export default Demo8
(9.React 新特性 useEffect 中的 componentWillUnmount (副作用)&)
9.React 新特性 useEffect 中的 componentWillUnmount (副作用)&
Demo9.jsx 清除副作用
import React ,{ Component,useState,useEffect } from "react"
const MyAPI = {
count:0,
subscribe(cb){
this.intervalId = setInterval(() =>{
this.count += 1;
cb(this.count)
},1000)
},
unSubscribe(){
clearInterval(this.intervalId);
this.reset();
},
reset(){
this.count = 0;
}
}
// export default class Demo9 extends Component{
// state = {
// count:0
// }
// componentDidMount(){
// MyAPI.subscribe(count =>{
// this.setState({
// count:count
// })
// })
// }
// componentWillUnmount(){
// // 清除定时器
// MyAPI.unSubscribe();
// }
// render(){
// return(
// <div>
// { this.state.count }
// </div>
// )
// }
// }
const Demo9 = () =>{
const [count,setCount] = useState(0);
useEffect(() => {
MyAPI.subscribe(currentCount => {
setCount(currentCount);
})
// 副作用的处理方案
return () => {
// 清除掉定时器
MyAPI.unSubscribe();
};
}, [])
return(
<div>
{ count }
</div>
)
}
export default Demo9
(10.React 新特性 React.memo&)
10.React 新特性 React.memo&
MemoDemo.jsx PureComponent
import React from "react"
import Child from "./Child"
// https://zh-hans.reactjs.org/docs/react-api.html#reactpurecomponent
export default class MemoDemo extends React.PureComponent{
state = {
time:new Date()
}
componentDidMount(){
setInterval(() =>{
this.setState({
time:new Date()
})
},1000)
}
render(){
console.log("render");
return(
<div>
<Child seconds={1}/>
{ this.state.time.toString() }
</div>
)
}
}
Child.jsx React.memo(Child)
import React from "react"
const Child = ({seconds}) =>{
console.log("child render");
return <p>current time:{seconds}</p>
}
export default React.memo(Child)
(11.React 新特性 useCallback优化&)
11.React 新特性 useCallback优化&
Demo11.jsx useCallback受限事件
import React,{useState,useCallback} from "react"
// https://zh-hans.reactjs.org/docs/hooks-reference.html#usecallback
const Demo11 =() =>{
const [ count,setCount ] = useState(0);
const [ count1,setCount1 ] = useState(0);
function clickHandler(){
setCount(count+1)
}
// 第二个参数决定了是否允许第一个参数执行:setCount1(count1+1)
// 如果count发生变化则允许执行,否则则不允许执行
// 第一个参数第一次会执行一次,之后才会判断第二个参数是否发生变化
return(
<div>
<p>{ count }</p>
<button onClick={ clickHandler }>Click me</button>
<p>{ count1 }</p>
<button onClick={ useCallback(() => setCount1(count1+1),[count])}>Click me</button>
</div>
)
}
export default Demo11
(12.React 新特性 useReducer&)
12.React 新特性 useReducer&
Demo12.jsx useReducer
import React,{ useState,useReducer } from "react"
// https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer
const initialState = { count:0 };
function reducer(state,action){
switch(action.type){
case "incrment":
return { count:state.count+1 }
case "decrement":
return { count:state.count-1 }
default:
throw new Error();
}
}
function Counter(){
const [state,dispatch] = useReducer(reducer,initialState);
return(
<div>
Count:{ state.count }
<button onClick={() => dispatch({ type:"incrment" })}>+</button>
<button onClick={() => dispatch({ type:"decrement" })}>-</button>
</div>
)
}
export default Counter
(13.React 新特性 useContext&)
13.React 新特性 useContext&
MainPage.jsx MyContext.provider组件与组件间传递数据
import React from "react"
import ChildPage from "./ChildPage"
// props 路由 自定义事件
export const MyContext = React.createContext();
function MainPage(){
return(
<div>
<MyContext.Provider value="Hello React useContext">
<ChildPage />
</MyContext.Provider>
</div>
)
}
export default MainPage
ChildPage.jsx
import React,{ useContext } from "react"
import { MyContext } from "./MainPage"
function ChildPage(){
return(
<p>{ useContext(MyContext) }</p>
)
}
export default ChildPage
(14.React 新特性 contextType&)
14.React 新特性 contextType&
Demo14.jsx ThemeContext
import React,{createContext} from "react"
const ThemeContext = createContext();
export default class Demo14 extends React.Component{
state = {
theme:"red"
}
render(){
const { theme } = this.state;
return(
<div>
<ThemeContext.Provider value={ theme }>
<Middle></Middle>
</ThemeContext.Provider>
</div>
)
}
}
class Middle extends React.Component{
render(){
return(
<div>
<Bottom></Bottom>
</div>
)
}
}
// class Bottom extends React.Component{
// render(){
// return(
// <div>
// <ThemeContext.Consumer>
// {
// theme => <h1>{ theme }</h1>
// }
// </ThemeContext.Consumer>
// </div>
// )
// }
// }
class Bottom extends React.Component{
static contextType = ThemeContext;
render(){
const theme = this.context
return(
<div>
<h1>{ theme }!!!</h1>
</div>
)
}
}
(15.React state深入理解&)
15.React state深入理解&
Demo15.jsx setState异步解决方案
import React from "react"
export default class Demo15 extends React.Component{
constructor(props){
super(props);
this.state = {
count:0
}
}
// setState当前的情况下,是异步操作
// setState同步和异步问题
// 合并所有的异步执行,然后异步执行完毕之后,才会执行异步回调函数
componentDidMount(){
this.setState({
count:this.state.count+1
},() =>{ // 回调函数
console.log(this.state.count);
})
this.setState((prevState,props) =>({
count:prevState.count + 1
}))
this.setState((prevState,props) =>({
count:prevState.count + 1
}))
this.setState((prevState,props) =>({
count:prevState.count + 1
}))
}
render(){
const { count } = this.state;
return(
<div>
{ count }
</div>
)
}
}
(React Dva框架&)
React Dva框架&
1.dva介绍与环境搭建
(dva介绍与环境搭建&)
dva介绍与环境搭建&
搭建dva环境,dva-cli创建项目
Dva new 创建项目
2.dva中引入antd
(dva中引入antd&)
dva中引入antd&
通过dva安装antd,antD按需加载
.webpackrc是配置文件处理跨域等等
初始加载的页面
引入Button,dva中引入antd标签
3.dva路由配置
(dva路由配置&)
dva路由配置&
加载路由
路由配置与以前一样
switch路由
直接加载router,default是一个function,通过default才能有routeConfig
路径分配
修改路径
多出一层路径
创建新的ProductPage文件夹
再次引用路由即可,exact精准匹配/只有准确匹配/才会匹配第一个
#切换history为browserHistory,修改入口
不需要#号了
- 忽略#号
- history报错
4.dva了解路由原理
(dva了解路由原理&)
dva了解路由原理&
history路由跳转
指定跳转路径
5.dva编写UI Component组件
(dva编写UI Component组件&)
dva编写UI Component组件&
Dva编写组件,页面跳转组件放在routes中,UI组件在Component中
React Hook新特性
6.dva Model创建
(dva Model创建&)
dva Model创建&
Model元素
Redux中实现actions和reducers增加store提取公共的常量,actions动作单独提取会创建很多文件。文件与文件之间关系不清晰
Dva把所有内容放在一起,创建一个文件即可。
Namespace命令空间,state初始化数据可以在入口文件进行声明
引用关系require进来,引入Model
State的productList中就有值了,product是命名空间
读取通过connect进行连接
Reducers进行更新数据,不能直接操作state,通过深度拷贝操作state。
数据是操作时action传递过来的,最好不要直接操作action,通过action.playload进行操作,数据是调用updateList时再来进行传递数据payload
Return当前操作好的返回给state,state就会被覆盖更新
UI视图操作state,通过action获取参数
再UI中操作当前要改变的redux,首先要connect连接
mapStateToProps从state中读取productList数据,通过命名空间获取productList数据
整个redux操作都是props
props与connect
传递数据
接收数据进行遍历,查看数据结构进行遍历
报错纠正
数据类型是对象,有一个name中有文本
声明好reducers可以直接操作reducers,渲染和操作要在一个页面内,redux的关联在父组件,需要在把操作状态传递到子组件。
不用再子组件定义connect连接redux和react
传递dispatch
dispatch传递和redux关联
子组件传递dispatch undefined,直接通过this.props.dispatch获取
在button按钮中获取商品列表,点击调用dispatch触发state,type指定action的操作
点击什么就能获得什么,直接通过model中的namespace,就可以索引updateList,不需要再抽离出来了。
通过product/updateList直接找到对应的函数,type点击操作的就是updateList方法。
传递action参数,读取的是payload
增加一条数据
7.dva路由跳转
(dva路由跳转&)
dva路由跳转&
路由的跳转规则,第一种方式是通过Link的方式
把Button换成Link
To跟地址
第二种方式事件跳转,原生路由进行,push跟路径
Product页面没有直接被路由包裹,被包裹的是ProductList页面
获取history
传递history到子product页面
点击跳转
传递太多子页面history会出现问题,通过withRouter方式进行跳转
没有传递history依然可以拿到数据
通过routerRedux依然可以进行路由跳转
withRouter跳转
Dva提供跳转方案
8.dva Model异步处理
(dva Model异步处理&)
dva Model异步处理&
saga异步操作,reducer中不能进行异步操作
effect异步操作传递参数payload
Effect异步操作处理方案,type操作updateList,要传递action参数,playload就是传递参数的点,外部就可以进行调用异步方法了
操作该方法
Dispatch调用方法执行,指向的是updateListAsync
增加一个数据,大多数数据来源于网络请求再去进行赋值
9.dva Mock数据处理
(dva Mock数据处理&)
dva Mock数据处理&
内置的网络请求
mock数据
增加post的处理
访问query就能拿到数据
声明数据
引用数据展示,引入进来就能进行访问了
通过getProduct()获取数据
生命周期访问数据
不用再启动一个服务器访问,已经集成在内,直接调用方法即可
Post请求,返回数据可以有参数
通过...require引入
通过流引入读取,file文件操作,把mock中所有js文件依次读取进来
使用Mock进行处理,@id随机的id @last随机昵称
引入mock
随机模拟数据
10.dva 网络请求
(dva 网络请求&)
dva 网络请求&
增加网络异步操作
Payload不变的,网络请求获取数据,playload就是调用方法传递的参数,有就传递没有就忽略
拿到数据,有数据就put,指明type,playload就是data数据
payload参数传递与action
网络接口返回数据
前端进行调用
Dispatch调用,命名空间进行调用,playload传递的参数,随便传递一个参数
读取参数,query里就是参数了
已经把参数payload传递给了getProduct
获取id1001
Get请求处理,接收参数
11.dva Model API
(dva Model API&)
dva Model API&
订阅监听效果
subscription监听
一上来就有action操作可以拿去值
Setup与叫什么名字没有关系
可以做很多操作,操作dispatch
监听window窗口的变化监听dispatch
window.onresize()
调用dispatch事件的执行
视图操作
可以不用添加命名空间
History对象监听参数,subscriptions触发就能监听
setupHistory
主页引入Model进行合并
合并model,获取所有的js文件
导入每一个key中的default
12.dva redux-actions
(dva redux-actions&)
dva redux-actions&
react-action简化actions
整合action操作,所有action集中在一个文件中
只有3个API
用increment()的方式代替type
合并action
引用使用
代替整个dispatch操作,如果哟参数指定payload
直接dispatch一个函数
(11.React TypeScript&)
React TypeScript&
(TypeScript+Node&)
TypeScript+Node&
标签:实战,count,const,dva,企业级,React,state,return From: https://blog.51cto.com/u_11837698/6081999