react学习笔记
基础篇
1、条件渲染
const isLoading = true;
const loadData = () => {
if (isLoading) {
return <div> loading...</div>;
}
return <div>数据加载完成。。。</div>;
};
const loadData2 = () => {
return isLoading?(<div> loading...</div>):(<div>数据加载完成。。。</div>)
};
const loadData3 = () => {
return isLoading&&(<div> loading...</div>)
};
const title = (
<div>
<h1>条件渲染</h1>
{loadData3()}
</div>
);
2、列表渲染
vue 的思想就是 v-for
这里值得提一下的是,key不建议使用 index (索引),尽量使用不可变的数据
import React from "react";
import ReactDom from "react-dom";
// const title = React.createElement('h1', null, 'Hello React !!!!')
const title = (
<h1>
{" "}
hello jsx <span id="span"> span </span>
</h1>
);
const songs = [{ id: 1, name: "向阳花"},{id: 2, name: "鼓楼"},{id: 3, name: "梵高先生" }];
const list=(<div> {songs.map((item,index)=><div key={index}>{item.name}</div>)}</div>)
ReactDom.render(title, document.getElementById("root"));
ReactDom.render(list,document.getElementById("span"))
3、样式处理
index.css
.title{
text-align: center;
}
const blue='blue'
const title = (
<div className="title" style={{'color':'red','background-color':blue}}>
样式处理
</div>
);
ReactDom.render(title, document.getElementById("root"));
4、组件
1、函数组件
//大写字母开头,必须有返回值
const Hello=()=>{
return(<div>这是一个组件 </div>)
}
ReactDom.render(<Hello/>, document.getElementById("root"));
2、类组件
/**
* 继承 React.Component,从而使用父类的属性及其方法
* 必须实现render ,并且有返回值
*/
class Hello extends React.Component{
render(){
return (<div>Hello class Component</div>)
}
}
ReactDom.render(<Hello/>, document.getElementById("root"));
3、抽离单独的js文件
component/Hello.js
import React from "react";
class Hello extends React.Component {
render() {
return <div> Hello class Component </div>;
}
}
export default Hello;
import Hello from './component/Hello'
ReactDom.render(<Hello/>, document.getElementById("root"));
4、有无状态组件
无状态组件:函数组件,只负责数据展示
有状态组件:类组件,负责更新UI
状态:state,即数据
5、state与setState()
class Hello extends React.Component {
handdleClik = () => {
this.setState({
count:this.state.count+1
})
this.state.count++
};
state= { count: 1 };
render() {
return (
<div>
{" "}
<div onClick={this.handdleClik}>计数器: {this.state.count}</div>
</div>
);
}
}
6、this指向
1、箭头函数
handdleClik2() {
this.setState({
count: this.state.count + 1,
});
this.state.count++;
}
<div onClick={() => this.handdleClik2()}>
计数器2: {this.state.count}
</div>
2、Funtion.prototype.bind()
利用ES5中的bind方法,将事件处理程序中的 this与组件实例绑定到一起
constructor(){
super()
this.state={
count:0
}
this.handdleClik=this.handdleClik.bind(this)
}
handdleClik () {
this.setState({
count: this.state.count + 1,
});
this.state.count++;
};
3、类的实例方法
5、事件处理
1、事件绑定
const handdleClik1 = () => {
console.log("单击事件触发1");
};
class Hello extends React.Component {
handdleClik2 = () => {
console.log("单击事件触发2");
};
render() {
return (
<div>
{" "}
<div onClick={handdleClik1}>handdleClik1</div>
<div onClick={this.handdleClik2}>handdleClik2</div>
</div>
);
}
}
2、事件对象(合成事件)
handdleClik3 = (e) => {
//阻止浏览器默认行为
e.preventDefault();
console.log("单击事件触发3");
};
<a href="https://www.baidu.com" onClick={this.handdleClik3}>
handdleClik3
</a>
3、表单处理
class Hello extends React.Component {
handleOnChange = (e) => {
this.setState({
text: e.target.value,
});
};
handleOnChange2 = (e) => {
this.setState({
context: e.target.value,
});
};
cityChange = (e) => {
this.setState({
city: e.target.value,
});
};
isCheckedChange = (e) => {
this.setState({
isChecked: e.target.checked,
});
};
state = { text: "1", context: "", city: "bj", isChecked: false };
render() {
return (
<div>
<input
type="text"
value={this.state.text}
onChange={this.handleOnChange}
/>
<br />
<textarea
value={this.state.context}
onChange={this.handleOnChange2}
></textarea>
<br />
<select value={this.state.city} onChange={this.cityChange}>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
<br />
<input
type="checkbox"
checked={this.state.isChecked}
onChange={this.isCheckedChange}
/>
</div>
);
}
}
4、多表单元素的事件处理
class MoreInput extends React.Component {
handleOnChange = (e) => {
const target = e.target;
const value =
e.target.type === "checkbox" ? e.target.checked : e.target.value;
const name = target.name;
this.setState({
[name]: value,
});
};
state = { text: "1", context: "", city: "bj", isChecked: false };
render() {
return (
<div>
<input
type="text"
name="text"
value={this.state.text}
onChange={this.handleOnChange}
/>
<br />
<textarea
value={this.state.context}
onChange={this.handleOnChange}
name="context"
></textarea>
<br />
<select
value={this.state.city}
onChange={this.handleOnChange}
name="city"
>
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="gz">广州</option>
</select>
<br />
<input
type="checkbox"
checked={this.state.isChecked}
onChange={this.handleOnChange}
name="isChecked"
/>
</div>
);
}
}
5、非受控组件
class Uncontrolled extends React.Component {
constructor() {
super()
//创建ref
this.txtRef = React.createRef()
}
getTxtRef = () => {
console.log("当前的值是:", this.txtRef.current.value)
}
render(){
return (
<div>
<input type="text" ref={this.txtRef}></input>
<button onClick={this.getTxtRef}>获取文本框的值</button>
</div>
)
}
}
进阶篇
1、组件传参
1、props示例
export default class PropsTest extends React.Component {
render(){
console.log(this.props)
return(
<div>接受到的数据:{this.props.name} {this.props.tag}</div>
)
}
}
<PropsTest
name="无限"
age={age}
color={[1, 2, 3, 4]}
fn={() => {
console.log("hello");
}}
tag={<p>哈哈哈哈</p>}
/>
2、父组件传递子组件
Child.js
import React from "react";
export default class Child extends React.Component {
render() {
return <div>我是Child,接收到父亲的消息: {this.props.name}</div>;
}
}
Parent.js
import React from "react";
import Child from "./Child";
export default class Parent extends React.Component {
render() {
return (
<div>
parent
<br /> <Child name={"我是你爹"}></Child>
</div>
);
}
}
3、子组件传递父组件
Child.js
export default class Child extends React.Component {
state = { count: 100 };
getMsg = () => {
this.props.getMsg(`给我零花钱${this.state.count}元`);
};
render() {
return (
<div>
我是Child,接收到父亲的消息: {this.props.name}
<br />
<button onClick={this.getMsg}>向我爹要零花钱</button>
</div>
);
}
}
Parent.js
export default class Parent extends React.Component {
state = { count: 0 };
getChildMsg = (data) => {
console.log("收到兔崽子的消息", data);
this.setState({
count: data,
});
};
render() {
return (
<div>
parent
<br /> <Child name={"我是你爹"} getMsg={this.getChildMsg}></Child>
<br />
<div>我儿子向我要多少钱:{this.state.count}</div>
</div>
);
}
}
4、兄弟组件之间的传递
Brother.js
export default class Brother extends React.Component {
getMsg = () => {
this.props.getMsg('微笑拥抱每一天,做像向日葵般温暖的女子');
};
render() {
return (
<div onClick={this.getMsg}>
我是Brother,发送给妹妹的消息
<br />
</div>
);
}
}
Sister.js
import React from "react";
export default class Sister extends React.Component {
render() {
return (
<div>
我是Sister,接收到哥哥的消息: {this.props.msg}
<br />
</div>
);
}
}
parent.js
export default class Parent extends React.Component {
state = { count: 0, childMsg: "" };
getChildMsg = (data) => {
console.log("收到兔崽子们之间的消息", data);
this.setState({ childMsg: data });
};
render() {
return (
<div>
<Sister msg={this.state.childMsg}/>
<Borther getMsg={this.getChildMsg} />
</div>
);
}
}
5、Context(跨组件传递)
//创建两个组件
const { Provider, Consumer } = React.createContext();
class App extends React.Component {
render() {
return (
<div>
<Provider value="pink">
{" "}
<Node />
</Provider>
</div>
);
}
}
const Node = () => {
return (
<div className="node">
<SubNode />
</div>
);
};
const SubNode = () => {
return (
<div className="SubNode">
<Child />
</div>
);
};
const Child = (props) => {
return (
<div className="child">
<Consumer>{(data) => <span>我是子节点 ---- {data}</span>}</Consumer>
</div>
);
};
6、props深入
1、children
class App extends React.Component {
render() {
this.props.children()
return (
<div>
子节点:
<br />
{/* {this.props.children} */}
<br />
<Test></Test>
</div>
);
}
}
const Test = () => {
return <button>button</button>;
};
ReactDom.render(
<div>
<App>
{/* 我是子节点{" "} */}
{() => {
console.log("这个是一个子节点的函数打印体");
}}
</App>
</div>,
document.getElementById("root")
);
2、props校验
默认的props没有类型约定,开发中容易出现一些错误并且无法定位,使用第三方的包,可以添加类型判断
npm i -S prop-types
import PropTypes from 'prop-types';
App.prototype={
color:PropTypes.array
}
约束规则
常见类型:
-
array、bool、func、number、object、string
-
元素类型:
-
element
-
必填项:
-
isRequired
-
特定结构的对象:shape({})
3、props默认值
App.defaultProps={
num:10
}
<App num={20}> </App>
2、生命周期
1、创建时(挂载阶段)
执行顺序: constructor ==> render ==> componentDidMount
钩子函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建时,最先执行 | 初始化state,为事件处理绑定this |
render | 每次渲染触发 | 渲染UI,不能调用setState() |
componentDidMount | 组件挂载(完成DOM渲染)后 | 发送网络请求,DOM操作 |
class App extends React.Component {
constructor(props) {
super(props);
console.log("生命周期:constructor");
}
render() {
console.log("生命周期:render");
return (
<div>
<h1> 统计打豆豆的次数</h1>
<button>打豆豆</button>
</div>
);
}
componentDidMount() {
console.log("生命周期:componentDidMount");
}
}
2、更新时(更新阶段)
执行时机 setState(),forceUpdate(),组件接收到新的props
执行顺序: render ==> componentDidUpdate
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次渲染触发 | 渲染UI,与挂载阶段是同一个 |
componentDidUpdate | 组件更新,完成DOM渲染之后 | 发送网络请求,DOM操作 |
class Child extends React.Component {
render() {
return <div> 我是子组件</div>;
}
componentDidUpdate(prevProps) {
console.log("生命周期:componentDidUpdate");
console.log("old:", prevProps.count, "new", this.props.count);
}
}
class App extends React.Component {
addCount = () => {
this.setState({
count: this.state.count + 1,
});
};
state = { count: 0 };
render() {
return (
<div>
<h1> 统计打豆豆的次数{this.state.count}</h1>
<button onClick={this.addCount}>打豆豆</button>
<Child count={this.state.count}></Child>
</div>
);
}
}
3、卸载时(卸载阶段)
组件从页面消失
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载,从页面消失 | 执行清理工作,比如计时器等 |
//子组件
componentDidMount() {
console.log("生命周期:componentDidMount");
this. time = setInterval(() => {
console.log("定时器触发~~");
}, 1000);
}
componentWillUnmount() {
console.log("死了。。。");
clearInterval(this.time)
}
//父组件
render() {
console.log("生命周期:render");
return (
<div>
{this.state.count > 3 ? (
<p>别打了,已经死了 </p>
) : (
<Child count={this.state.count}></Child>
)}
<h1> 统计打豆豆的次数{this.state.count}</h1>
<button onClick={this.addCount}>打豆豆</button>
</div>
);
}
3、render-props
1、render-props模式、及其组件复用
class Mouse extends React.Component {
state = { x: 0, y: 0 };
handleMouseMover = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
});
};
render() {
return this.props.render(this.state);
}
componentDidMount() {
window.addEventListener("mousemove", this.handleMouseMover);
}
}
class App extends React.Component {
render() {
return (
<div>
<Mouse
render={(mouse) => {
return (
<p>
鼠标位置 x:{mouse.x} y:{mouse.y}
</p>
);
}}
></Mouse>
<Mouse
render={(mouse) => {
return (
<img
src="http://picsum.photos/50/50?random=1"
style={{
position: "absolute",
left: mouse.x - 25,
top: mouse.y - 25,
}}
/>
);
}}
></Mouse>
</div>
);
}
}
2、children替换render属性
class Mouse extends React.Component {
state = { x: 0, y: 0 };
handleMouseMover = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
});
};
render() {
return this.props.children(this.state);
}
componentDidMount() {
window.addEventListener("mousemove", this.handleMouseMover);
}
}
class App extends React.Component {
render() {
return (
<div>
<Mouse>
{(mouse) => {
return (
<p>
鼠标位置 x:{mouse.x} y:{mouse.y}
</p>
);
}}
</Mouse>
<Mouse>
{(mouse) => {
return (
<img
src="http://picsum.photos/50/50?random=1"
style={{
position: "absolute",
left: mouse.x - 25,
top: mouse.y - 25,
}}
/>
);
}}
</Mouse>
</div>
);
}
}
4、高阶组件
1、基本使用
function WithMouse(WrappedComponent) {
class Mouse extends React.Component {
state = {
x: 0,
y: 0,
};
handleMouseMove = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
});
};
componentDidMount() {
window.addEventListener("mousemove", this.handleMouseMove);
}
render() {
return <WrappedComponent {...this.state}></WrappedComponent>;
}
}
return Mouse;
}
const Position = (props) => {
return (
<p>
x:{props.x}y:{props.y}
</p>
);
};
const MousePosition=WithMouse(Position)
ReactDom.render(
<div>
<MousePosition></MousePosition>
</div>,
document.getElementById("root")
);
2、displayNmae
//获取name
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayname || WrappedComponent.name || `Component`;
}
function WithMouse(WrappedComponent) {
class Mouse extends React.Component {
state = {
x: 0,
y: 0,
};
handleMouseMove = (e) => {
this.setState({
x: e.clientX,
y: e.clientY,
});
};
componentDidMount() {
window.addEventListener("mousemove", this.handleMouseMove);
}
render() {
return <WrappedComponent {...this.state}></WrappedComponent>;
}
}
Mouse.displayname = `WithMouse${getDisplayName(WrappedComponent)}`;
return Mouse;
}
3、传递props
render() {
return <WrappedComponent {...this.state} {...this.props}></WrappedComponent>;
}
5、路由
1、基本使用
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const First = () => {
return <div>第一个页面</div>;
};
class App extends React.Component {
render() {
return (
<Router>
<div>App</div>
{/* 指定路由入口 */}
<Link to="/first">页面一</Link>
{/* 指定路由出口 */}
<Route path="/first" component={First}></Route>
</Router>
);
}
}
2、常用组件说明
- Router组件:包裹整个应用,一个React应用只需要一个
- 两种常用的Router: HashRouter(#)和BrowserRouter
- HashRouter:使用URL的哈希值实现 127.0.0.1:3000/#/frist
- BrowserRouter:使用H5的history API实现 127.0.0.1:3000/frist
- Like:指定导航连接
- Route :指定路由展示组件
3、编程式导航(js实现路由切换)
class Login extends React.Component {
toHome = () => {
this.props.history.push("/home");
};
render() {
return (
<div>
{" "}
登录页面
<br />
<button onClick={this.toHome}>登录</button>
</div>
);
}
}
const Home = (props) => {
const goBack = () => {
props.history.go(-1);
};
return (
<div>
{" "}
首页
<br />
<button onClick={goBack}>返回登录页面</button>{" "}
</div>
);
};
class App extends React.Component {
render() {
return (
<Router>
<div>
<Link to="/login"> 登录 </Link>
<Route path="/login" component={Login}></Route>
<Route path="/home" component={Home}></Route>
</div>
</Router>
);
}
}
4、默认路由
<Route path="/" component={Home}></Route>
5、withRouter
在非路由组件获取当前路由path时需要这个组件处理
/**
* 包装非路由组件,
* 返回新的组件
* 新的组件包含三个属性 history,location,match
*/
//获取当前的路由path
const path=this.props.location.pathname
export default withRouter(leftNav);
6、匹配模式
1、模糊匹配
类似与vue-router的子路由与父路由
const A = () => {
return <div>页面A</div>;
};
const AC1 = () => {
return <div>页面A的子页面1</div>;
};
const AC2 = () => {
return <div>页面A的子页面2</div>;
};
class App extends React.Component {
render() {
return (
<Router>
<div>
<Link to="/a"> to页面A </Link>
<Link to="/a/ac1">to页面A的子页面1</Link>{" "}
<Link to="/a/ac2">to页面A的子页面2</Link>
<Route path="/a" component={A}></Route>
<Route path="/a/ac1" component={AC1}></Route>{" "}
<Route path="/a/ac2" component={AC2}></Route>
</div>
</Router>
);
}
}
2、精准路由
只显示子路由不显示父路由
//添加关键字 exact
<Route path="/a" exact component={A}></Route>
7、路由守卫
首先明确,在之前的版本中,React Router 也提供了类似的 onEnter
钩子,但在 React Router 4.0 版本中,取消了这个方法。React Router 4.0 采用了声明式的组件,路由即组件,要实现路由守卫功能,就得我们自己去写了。
BeforeEach.jsx
import { Route, Redirect } from "react-router-dom";
import React, { Component } from "react";
class BeforeEach extends Component {
render() {
const { location, config } = this.props;
const { pathname } = location;
const isLogin = true;
console.log(pathname);
// 如果该路由不用进行权限校验,登录状态下登陆页除外
// 因为登陆后,无法跳转到登陆页
// 这部分代码,是为了在非登陆状态下,访问不需要权限校验的路由
const targetRouterConfig = config.find((v) => v.path === pathname);
if (targetRouterConfig && !targetRouterConfig.auth && !isLogin) {
const { component } = targetRouterConfig;
return <Route exact path={pathname} component={component} />;
}
if (isLogin) {
// 如果是登陆状态,想要跳转到登陆,重定向到主页
if (pathname === "/login") {
return <Redirect to="/" />;
} else {
// 如果路由合法,就跳转到相应的路由
if (targetRouterConfig) {
return (
<Route path={pathname} component={targetRouterConfig.component} />
);
} else {
// 如果路由不合法,重定向到 404 页面
return <Redirect to="/404" />;
}
}
} else {
// 非登陆状态下,当路由合法时且需要权限校验时,跳转到登陆页面,要求登陆
if (targetRouterConfig && targetRouterConfig.auth) {
return <Redirect to="/login" />;
} else {
// 非登陆状态下,路由不合法时,重定向至 404
return <Redirect to="/404" />;
}
}
}
}
export default BeforeEach;
routerCconfig.js
import User from "../pages/user"; //暂时担任404的任务
import Home from "../pages/home";
import Login from "../pages/login";
export const routerConfig = [{
path: '/',
component: Home,
auth: true,
}, {
path: '/home',
component: Home,
auth: true,
}, {
path: '/login',
component: Login,
}, {
path: '/404',
component: User
}];
App.jsx
<Switch>
<BeforeEach config={routerConfig}></BeforeEach>
</Switch>
6、Redux
1、基本使用
count.jsx
import React from "react";
import store from "../../store/index";
import {createIncrementAction} from '../../store/count_action'
export default class Count extends React.Component {
state = {
wind: "北风6级",
};
increment = () => {
store.dispatch(createIncrementAction(1));
};
decrement = () => {
const action = { type: "decrement", data: 1 };
store.dispatch(action);
};
render() {
return (
<div>
<h2>当前求和:{store.getState()}</h2>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
);
}
}
store 文件夹
constant.js
export const INCREMENT = 'increment'
count_action.js
import {INCREMENT} from './constant'
//用来创建action函数
export const createIncrementAction = value => ({
type: INCREMENT,
data: value
})
count.js
export default function countReducer(preState = 0, action) {
const {
type,
data
} = action
switch (type) {
case 'increment':
return preState + data
case 'decrement':
return preState - data
default: //初始化
return preState
}
}
index.js
import {createStore} from 'redux'
import countReducer from './count'
const Store =createStore(countReducer)
export default Store
2、 react-redux
src\container\count\index.jsx
//ui组件的容器组件,用于UI连接Redux
import CountUi from '../../components/count/count'
import {connect} from 'react-redux'
import store from '../../store/index'
function a(){
return {count:store.getState()}
}
const CountContainer=connect(a)(CountUi)
export default CountContainer
src\components\count\count.jsx
import React from "react";
export default class Count extends React.Component {
state = {
wind: "北风6级",
};
increment = () => {};
decrement = () => {};
render() {
return (
<div>
<h2>当前求和:{this.props.count}</h2>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
);
}
}
src\index.js
import React from "react";
import ReactDOM from "react-dom";
//import Count from "./components/count/count";
import Count from './container/count/index'
import store from "./store/index";
//当状态发生变化,必定调用整个方法
store.subscribe(() => {
ReactDOM.render(
<div>
app
<br />
<Count store={store}></Count>
</div>,
document.getElementById("root")
);
});
ReactDOM.render(
<div>
app
<br />
<Count store={store}></Count>
</div>,
document.getElementById("root")
);