react基础使用

1. 不再使用react.createElement

使用jsx创建对象。并最后使用ReactDom.render(param1, param2)去对对象渲染。其中param1为js创建的变量,param2为原生dom方法选中的html元素。

在jsx中的html部分使用js变量等js语法应外加大括号。
render后会接diff.render并非重头对所有元素进行渲染,只会挑出其与之前变化的部分进行重新渲染.


2. map对数组批量操作

类似foreach、map实现对js数组进行批量化操作。给定数组list,使用方法为list.map(item => target),target为目标变量。
在使用map的时候应该加入key,一般是对html元素添加key属性,key属性的内容是特异的。
map不仅自执行循环,同时可以用来做return直接渲染。
map的箭头函数必须要有返回值。


3. 组件写法

1
2
3
4
5
6
7
class Component extends React.Component {
render () {
return (
<div>component here</div>
)
}
}

注意到类名首字母必须大写、必须提供render方法以及必须有返回值。在渲染的时候将原param1改为<Component />这样的方式。事实上<Component />这样的写法在代码中都是对组件的调用,并不局限于渲染函数。
如果在独立js写组件,开头应import React …,在这里要暴露几个组件,写法为export default class YourClassName extends React.Component{}


state和setState注意事项

在组件html代码中可以添加事件。事件内容应为this.functionName,其中functionName为本类下的类方法,注意此处事件内容后不需添加括号,但仍需外侧方括号。在functionName括号中的变量即为当前事件对象。
在组件中的状态初始化可以使用简写,即直接使用

1
2
3
state = {
var : 0
}

应当注意,为了性能起见,state应当只存放与渲染有关的数据,其余数据如要在多个方法中使用应放到this中.
在类内其他地方调用state中属性应通过this.state.var使用,且state私有。state的修改不能直接通过访问变量直接操作进行修改,需要通过

1
2
3
this.setState({
var : this.state.var + 1
})

也可以利用扩展运算符新建对象,在新对象中修改并对原来state赋值,这样就能安全地修改state.
诸如此类的操作进行修改。但这样会带来一个问题。比如在button指定了onClick事件,事件函数func内部需要修改state。这个时候应该将事件函数改写成
1
2
3
func **= () =>** {

}

箭头函数。这样可以避免不必要的麻烦。
setState函数是异步更新的,所以不要依赖另一个setstate来写当前的setState.
如果想要setState依赖于前一个state去写的话,写法如下:
1
2
3
4
5
this.setState((state, props) => {
return {
var : state.var + 1
}
})

虽然这么写可以让下面读到的state变化,但这仍然是异步的.
setState还有第二个参数.如果写上第二个参数,意为在重新渲染完之后进行的操作.写法例如:
1
2
3
this.setState((state, props) => {}, () => {
console.log('over rendering')
})

在return某些html对象的时候里面要插入语句,应该写成表达式,即用三元运算符替代if语句。换言之,return中的js只能写表达式。


在js中获取键值对中的值有特别的写法。例如键值对a = [k: ‘1’, m: ‘2’, n: ‘33’],想要获取两个数值只需要写入

1
const {k, m} = a //此处必须同名,获取之后可以直接使用变量k,m

扩展运算符:对参数对象进行遍历并取出所有可遍历属性,在前面加三个点,类似copy的操作。例如:
1
2
let bar = { a: 1, b: 2 };
let baz = { c: 3, ...bar }; // { c: 3, a: 1, b: 2 }

如果在一个数组类型中,前面是扩展运算符,后面的key和前面重叠意为修改前面扩展运算符的键值对.


可控组件

常用于表单处理。用法是写到input框中的onChange属性中的一个函数this.func。在func声明的时候写法会同上面不一样。

1
2
3
4
5
6
7
// 此处默认state仍同上
func = var2 => {
this.setState({
var : var2.target.value // 这样写是为了获取表单的值,且实时获取至类内state中,其中var2是对象,target是固有写法,value是对象属性
})
}
// 而且在input标签的value属性要写上value={this.state.var}

html中四种表单分别为input type=”text”、textarea(富文本框)、select(下拉框)、input type=”checkbox”(复选框),前三个的内容属性都为value,第四个是checked。
在多表单处理的时候,通常对不同的表单添加name属性,这样可以只写一个在onChange的函数并设置为多出口。写法为
1
2
3
4
const name = var2.target.name
this.setState({
[name] : yourTargetValue // 此处方括号直接字符串转变量
})

组件onClick等事件传参

这里的传参十分反人类。比如某个部件onClick要传参数,按照this.method(num)是不行的。必须写成
onClick = {e => this.method(e, num)},而且在method里面也得把e写上。


4.组件通信

这里仅说明类实现的组件通信。组件通信应该写在渲染部分,具体写在渲染的html对象那个参数里面,如
<component pr='hello' echo='nn' />
这样就能在class中去调用pr和echo这两个属性。在组件通信中,返回的是一个对象列表,使用关键字为this.props,如要调用具体内容,写为this.props.pr等。props传所有数据都可以,但只可读不可写。
如果类组件中重构过constructor还要使用props,就需要在constructor中的super(props)这样将props传入,否则拿不到,同时constructor的形参也要写props来接收。


如果在调用实例中不写为<component />而写为<component>content</component>,这里的content会成为props的一个元素,即props.children,如果这里的content是个函数,甚至可以props.children()来调用。这里了解即可,一般人不会这么写。
Props可以指定类似函数一样的默认值。当在实例化<component />时不指定props,而在外面加上
component.defaultProps={Var: key}
这样的语句,就默认在props里指定了Var: key这样的默认值。

父传递给子组件

在父组件调用子组件的时候像上面组件通信提到的写法即可传递。在子组件中props即为通信内容。
通信记得传key!且key在子组件props中读不到。还要指定另外的变量才能拿到key里的内容。

父组件调用子组件的信息

分三步完成。即在父组件写入调用函数及对调用信息的处理、写入子组件的对象参数(写入的是那个父组件中调用的函数)、在子组件中处理。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Father extends React.Component {
getChild = data => {
// deal with data
}
render () {
return(```<Child getKeyWord={this.getChild} />```)
}
}

class Child extends React.Component {
returnProps = () => {
this.props.getKeyWord('targetData') //注意到此处getKeyWord应和上面调用时的属性一致
}
render () {
return(<button onClick={this.returnProps}></button>)
}
}

兄弟组件相互通信

这个地方比较繁琐,但很好理解。比如Component1要与Component2通信,获取Component2数据,则要用到公共父类,其中公共父类提供state中的键值对让两者共享,还要提供方法让Component2调用来传Component2的数据。
具体操作为,在Component1中写入state的值,在Component2中调用父类提供方法,按上面说的父组件调用子组件去处理。简言之,Component1要获取Component2的数据,就是Component2先于父类通信传递信息到父类,再交给Component1。代码重复度和上面较高,不再举例。

跨组件通信

这一般是在远房亲戚(嵌套多层)情况下使用。先选定想要相互通信的两个组件。在发送方外部套上<Provider value='yourTargetValue'></Provider>,其中value这个关键字不能变。
但是比较反人类的是,接收方必须要在内部套上<Consumer>{data => <div>{yourDealingData}</div>}</Consumer>,其中最外层首字母大写和形式不能变。内部是一个函数的样子,用来接收数据。

props校验

就像py的assert一样,这被用于类型检查。比如在class App外边渲染的时候回传通信信息,我们想要对回传的信息进行格式校验,就在外侧写入校验字段。例如:

1
2
3
4
5
6
7
8
9
10
11
12
    App.propTypes = {
yourVarName: PropTypes.array //指定为数组类型
}
PropTypes的类型常用的有array、bool、func、number、object、string
如果对应的键值对必须存在的话,在指定类型后还应加上.isRequired
如果返回一个对象,对对象内部键值对有要求的话,例子如下:

yourVarName: PropTypes.shape({ // 这里的shape是固定写法。
var1: PropTypes.yourType,
var2: PropTypes.yourType,
// and so on
})


5.钩子函数

在创建组件对象时,按顺序为constructor(), render(), componentDidMount()。
其中constructor用于初始化state,render用于渲染(不能在render主部分调用setState,只能在return里调用),componentDidMount在完成渲染后调用,用于发送网络请求和DOM操作。

当setState触发,或forceUpdate()触发,或当前组件作为子组件收到新的props,这三种情况之一出现组件的render就会重新调用,然后componentDidMount也会在render调用完之后被调用一次。且setState调用多次,render也只会重新渲染一次,因为setState是异步的,出于性能考虑.
但这里应该注意,这个componentDidMount内的setState必须要有个if条件判断,不然会死循环。这里建议if里写一个参数为prevProps,这个参数应该在componentDidMount (prevProps) {}这一步的形参中写入。当prevProps的某个值和this.props的对应值不相等的时候再执行内部函数,否则直接return。这么写避免死循环。

在组件完成功能被析构的时候,钩子函数为componentWillUnmount。这常常被用于清理setInterval(计时器)等调用系统函数的操作。

在类内还有一个钩子函数名为shouldComponentUpdate(nextProps, nextState).内部return true即为可重新渲染.这个钩子函数在重新渲染前执行,即shouldCOmponentUpdate后再执行新的render().这个钩子函数的第二个参数比较有趣,this.state是当前的state,而nextState是更新后的状态.这一钩子函数的return前一般加一个if,用来优化性能,有的东西不必重新渲染.


6. render props

这被用于某个组件中部分功能的公共模板化,类似把相同的代码抽象成一个函数。具体使用见下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Son extends React.Component {
state = {
key: 1
}
dealWithState = () => {
everything u like
}
render () {
return this.props.render(this.state) //将state作为返回值返回给父类(这些state就是要复用的,暴露给组件外部),写法固定
}
}

class Father extends React.Component {
render () {
return(
<Son render={ //这个render只是变量名,一般都写成children而不写成render
var => { //这里的var实际上就是Son里面的state
return(<p>{var.key}</p>)
}
} />
)
}
}

在react中指定图片需要在头顶import pic from ‘yourPath’,然后在图片标签中的src写成src={pic}。
事实上这一封装操作相当于只依靠子组件的render函数中的返回值返回给父组件而已。相当于父索取信息,子返回信息。
建议对render props进行格式校验。即children: Proptypes.func.isRequired

7. 高阶组件

这个同样被用于模板化组件。分三步实现,以函数形式创建高阶组件模板,写出想要被套到模板上的组件和最终创建好了的组件。类似python装饰器。写法例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function withYourHOCName (WrappedComponent) { // 约定with开头
class yourBasicProvider extends React.Component {
state = {} // 想要返回的state
yourDealWithState = () => {} // 想要对state处理的操作
render () {
return <WrappedComponent {...this.state} {...this.props}/> // 这一行固定写法,为了回传state,也为了能在最外层父类与yourTargetComponent通信
}
}
yourBasicProvider.displayName = getDisplayName(WrappedComponent) // 让不同的套用有不同的名字,不然就都叫yourBasicProvider了,无法区分.
return yourBasicProvider // 将基础逻辑的那个类返回
}

const yourTargetComponent = props => ( // 此处的props就是上面的state,这里声明了想要被套在HOC的组件
<p>prop.yourProperties</p>
)

const yourResultComponent = withyourHOCName(yourTargetComponent) //类似装饰器的写法

得到最后的Component就是封装过yourBasicProvider的state这一个内容的高级组件。

8.路由

使用步骤:
导入。 import { BrowserRouter as Router, Route, Link, Routes } from ‘react-router-dom’
用路由标签包裹想要使用路由的整个最外层。即<Router></Router>.这可以在上面声明最外层组件的时候实现,也可以在最后ReactDom渲染的时候在外面加上也可以。
指定路由的入口,即用户要点击的东西。代码为<Link to='/yourTargetPage'>指示的文字</Link>.
指定路由出口。代码为

1
2
3
4
<Routes>
<Route path='/yourTargetPage' element={<yourTargetComponent />} />
<Route> // route可以有多个,但必须指定这两个关键字,同时Route最外面一定被Routes包着。
</Routes>

这些关键字的名称都不能改变,且to和path里的是同样的文字。
这个Route写到哪里,渲染的element就在对应位置,并不是真实的跳转,有点像ajax动态请求的味道。

嵌套路由

react v6的新写法属实让人头大。不知道出于什么原因,我的Outlet无法使用。
嵌套路由大概描述一下就是,主页面只写父组件(这里是第一个Routes),父组件path必须后面跟/*。父组件内部还有一个Routes,里面放着一个Route,不过子组件的path不用带上父组件Route的path前缀。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function App() {
return (
<Router>
<div className="App">
<ul>
<li><Link to='home'>首页</Link></li>
<li><Link to='citylist'>城市选择</Link></li>
</ul>
<Routes>
<Route path='home/*' element={<Home />} /> //这里的Home是父组件
<Route path='citylist' element={<CityList />} />
</Routes>
</div>
</Router>
)
}

at Home\index.js:
export default class Home extends React.Component {
render () {
return(
<div>
这是首页
<nav>
<Link to='news'>go to news</Link> //子组件,这里不写也行,直接输入url也可以跳转
</nav>
<Routes>
<Route path='news' element={<News />}/> //这个path不必带前缀,且这里是第二个Routes
</Routes>
</div>
)
}
}

react引用原生js

最近在做项目需要用到这个。react按照惯例,代码会放在src里。但是我的需求是,引用外链js里的函数,这就要求我们用原生js写法。为此,找到public/下的index.html,插入

1
2
3
4
5
6
<script type='type' src='www.baidu.com'></script>  // 在这里定义了一个foo函数,比如说
<script>
var bar = (param) => {
foo(param)
}
</script>

然后在src中的react框架js中,想调用这个foo函数就应该使用window.bar(YourParams)

react build之后部署在服务器

react build之前需要设置一个homepage在package.json里,设置为’./‘,不然index.html是白的。
而且要匹配路径的话,需要在router写一个attribute,其中具体为basename=’’,引号里是服务器匹配的路径。