说在最前面
本文是自己最近再学习React的总结和记录。尽力把每个示例代码都写了出来,尽力写的通俗易懂一些,也算是锻炼自己的讲述能力,据说给别人讲的话学的会更快,所以写下此文。
如有错误或者其他,还望各位批评指正
JSX
简介
在JS里面写的一些标签称为JSX语法
基本语法
在返回的时候不需要加引号
import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
// 基本
const jsx = <h1>hello react</h1>
// 变量
const name = '123456'
const jsxName = <h1>{ name }</h1>
// 函数
const user = { firstName: 'tom', lastName: 'jerry' };
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const jsxFunction = <h2>{ formatName(user) }</h2>
// 对象
const greet = <p>hello, Jerry</p>
const jsxObject = <h2>{ greet }</h2>
// 三元表达式
const showTitle = true;
const title = showTitle ? <h2>{ name }</h2> : null;
const jsxOthers = (<div>{
/* 条件语句句 */
} { title }
</div>);
// 循环
const arr = [1, 2, 3].map(num => <li key={ num }>{ num } </li>)
const jsxArray = (<div> {/* 数组 */ }
<ul>{ arr }</ul>
</div>)
// 注意
const jsxAtt = (<div>
{
/* 属性:静态值⽤用双引号,动态值⽤用花括号;class、for等 要特殊处理理。
* 像class需要写成className,for需要写成htmlFor,并且属性名需要采用驼峰命名法
* */
}
<img src={ logo } style={ { width: 100 } } className="img"/></div>)
// 函数传参
function greeting(name) {
if (name) {
return <h1>Hello, {name}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
let name2 = '测试';
const element = greeting(name2);
// 循环
let names = ['张三','李四','王五'];
let elements = [];
for(let i=0;i<names.length;i++){
elements.push(<li>{names[i]}</li>);
}
export { jsx, jsxName, jsxFunction, jsxObject, jsxOthers, jsxArray,jsxAtt,element,elements }
组件
可以将UI切分成一些独立的、可复用的部件,
class 组件
class组件通常拥有状态和生命周期,继承于Component,实现 render方法
基本
import React, {
Component } from 'react';
class Home extends Component {
render() {
return <h1>Hello, Class Components</h1>;
}
}
类组件中的状态管理(秒表实例)
import React, {
Component } from 'react';
class State extends Component {
// 使用state属性维护状态,在构造函数中初始化状态
constructor(props) {
super(props);
this.state = {
date: new Date(), number: 0 };
// this.changeNumber = this.changeNumber.bind(this)
}
// 组件挂载时启动定时器每秒更新状态
componentDidMount() {
this.timerID = setInterval(() => {
// 使⽤用setState方法更新状态
this.setState({
date: new Date() });
}, 1000);
}
// 组件卸载时停止定时器
componentWillUnmount() {
clearInterval(this.timerID);
}
// 这里不绑定this 会报错 两种方法 第一种 用箭头函数。第二种在constructor进行绑定声明
// setState是异步执行的。执行这个结果是 log 是2不是3 证明是异步执行
changeNumber = () => {
this.setState({
number: this.state.number + 1 } )
this.setState({
number: this.state.number + 2 } )
// 这里不能进行直接修改
console.log(this.state.number)
}
// 同步改变
changeNumber2 = ()=> {
this.setState((nextState)=>{
return {
number: nextState.number + 1 } } )
this.setState((nextState)=>{
return {
number: nextState.number + 2 } } )
console.log(this.state.number)
// 使用定时器
// setTimeout(() => { this.setState((nextState)=>{ return { number: nextState.number + 1 } } ); console.log(this.state.counter); }, 0);
// 原生事件中修改状态
// componentDidMount(){ document.body.addEventListener('click', this.setState((nextState)=>{ return { number: nextState.number + 1 } } ), false) }
}
render() {
return (
<div>
{
this.state.date.toLocaleTimeString() }
<p>{
this.state.number }</p>
<p>setState是异步执行的</p>
<button onClick={
this.changeNumber}>异步改变number</button>
<button onClick={
this.changeNumber2}>同步改变number</button>
</div>
)
}
}
状态的设置和改变
在constructor
设置状态。使用setState
来改变状态。示例如上
关于setState
注意,setState是 异步的!!!!
如何证明?
见示例代码中changeNumber
的描述和实现
如何实现同步?
常见的有三种方法:
- 利用函数传递参数
- 使用定时器
- 直接找到DOM元素直接修改
上述三种方法在示例代码中都可以找到。
函数的this 绑定
一般有两种方法
- 在
constructor
里面用bind
方法绑定。例如this.changeNumber = this.changeNumber.bind(this)
- 利用ES6的箭头函数。示例
changeNumber = () => { // 执行代码 }
。(**原因解释:**箭头函数默认指向定义它时,所处上下文的对象的this指向)
function组件
基本
import React from "react";
import User from "./pages/User";
function App() {
return (
<div>
<User />
</div>
);
}
export default App;
后文的 react HOOKs
会做详细解释
组件通讯
props通讯
适用于父子组件通讯
// index.js
ReactDOM.render(<App title="测试代码" />, document.querySelector('#root'));
// App.js
<h2>{
this.props.title}</h2> // 输出为 测试代码
Props 的只读性
官网原话 :组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props
状态提升
官方解释: 通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
实例代码
import React from 'react'
class Child_1 extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<h1>{
this.props.value+'child 1'}</h1>
</div>
)
}
}
class Child_2 extends React.Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<h1>{
this.props.value+'child 2'}</h1>
</div>
)
}
}
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {
txt:"我是父组件"
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){
this.setState({
txt:e.target.value
})
}
render(){
return (
<div>
<input type="text" value={
this.state.txt} onChange={
this.handleChange}/>
<p>{
this.state.txt}</p>
<Child_1 value={
this.state.txt}/>
<Child_2 value={
this.state.txt}/>
</div>
)
}
}
export default Parent
context 跨组件通讯
跨层级组件之间通信
单层传递
// ContextFather.js
import React from 'react';
import ContextChild from './child';
const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer
const data = {
name: '我是father数据',
}
function ContextApp() {
return (
<div>
<Provider value={
data }>
<Consumer>
{
value => <ContextChild {
...value}/>
}
</Consumer>
</Provider>
</div>
)
}
export default ContextApp
// child.js
import React, {
Component } from 'react';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
return (<h1>hello {
this.props.name}</h1>);
}
}
export default ContextChild
这样可以获取到传递过来的name
值
多层传递
// Context.js
import React from 'react';
const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer
export {
Provider,Consumer}
// ContextFather
import React from 'react';
import ContextChild from './child';
import {
Consumer,Provider} from './Context';
const data = {
name: '我是father数据',
}
function ContextApp() {
return (
<div>
<Provider value={
data }>
// 这个层级不需要就不用写 Consumer
<ContextChild/>
</Provider>
</div>
)
}
export default ContextApp
// child.js
import React, {
Component } from 'react';
import ContextChild2 from './child2';
import {
Consumer} from './Context';
class ContextChild extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>我是第一层</h1>
// 这里获取的是一个空值,没有数据
<h2>{
this.props.name}</h2>
// 这个层级需要就吧数据传递过去
<Consumer>
{
value => <ContextChild2 {
...value}/>
}
</Consumer>
</div>
)
}
}
export default ContextChild
// child2.js
import React, {
Component } from 'react';
class ContextChild2 extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props)
console.log('我是第二层的')
return (
<div>
<h1>我是第二层的</h1>
<h1>{
this.props.name}</h1>
</div>
);
}
}
export default ContextChild2
特别注意
- 不能有多个提供者,例如在每个文件里 写上
Context.js
里面的语句。则会获取不到数据。不同的provider提供的值不一致 - 在React的官方文档中,
Context
被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。不过,这并非意味着我们不需要关注Context。事实上,很多优秀 的React组件都通过Context来完成⾃自己的功能,比如react-redux
redux 类似Vuex
没学过Vue的可以理解为全局状态管理,即你在哪里都能访问的到(我个人是这么理解的)
知识扩展
Reducer
解释: reducer 就是一个纯函数,接收旧的 state
和 action
,返回新的 state
reduce
本例子来源于MDN
const array1 = [1, 2, 3, 4];
const reducer = (total, currentValue) => total + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
compose(没太理解)
compose
就是执行一系列的任务(函数)
思考
如何输出1 2 3
function f1() {
console.log("1"); }
function f2() {
console.log("2"); }
function f3() {
console.log("3"); }
第一种
f1();f2();f3();
第二种
f3(f2(f1()))
第三种
function compose(...funcs) {
// funcs 是一个函数列表
if (funcs.length === 0) {
console.log('empty');
} else if (funcs.length === 1) {
return funcs[0];
} else {
return funcs.reduce(
(left, right) => (...args) => {
// console.log(args)
// console.log(right)
// console.log(left)
right(left(...args)) // 相当于 f3(f2(f1()))
}
);
}
}
compose(f1,f2,f3)()
Redux
使用步骤
- 需要一个
store
来存储数据 store
里reducer
初始化state
并定义state
修改规则- 通过
dispatch
一个action
来提交对数据的修改 action
提交到reducer
函数里,根据传入的action
的type
,返回新的state
实例代码
// 创建store store/ReduxStore
import {
createStore } from 'redux';
// 创建数据并指定行为
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'add':
return state + 1
case 'minus':
return state - 1
default:
return state
}
}
// 创建一个store
const store = createStore(counterReducer)
export default store
// ReduxPage
import React, {
Component } from 'react';
import store from '../store/ReduxStore';
export default class ReduxPage extends Component {
componentDidMount() {
// 挂在之后
store.subscribe(() => {
// 执行订阅
console.log('subscribe');
this.forceUpdate(); // 重新render
//this.setState({}); // 这个也可以,内部方法也是forceUpdate
})