react基础使用

2022-10-28 11:27:00 浏览数 (1)

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. 组件写法

代码语言:javascript复制
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括号中的变量即为当前事件对象。 在组件中的状态初始化可以使用简写,即直接使用

代码语言:javascript复制
state = {
    var : 0
}

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

代码语言:javascript复制
this.setState({
    var : this.state.var   1
})

也可以利用扩展运算符新建对象,在新对象中修改并对原来state赋值,这样就能安全地修改state.

诸如此类的操作进行修改。但这样会带来一个问题。比如在button指定了onClick事件,事件函数func内部需要修改state。这个时候应该将事件函数改写成

代码语言:javascript复制
func **= () =>** {

}

箭头函数。这样可以避免不必要的麻烦。

setState函数是异步更新的,所以不要依赖另一个setstate来写当前的setState.

如果想要setState依赖于前一个state去写的话,写法如下:

代码语言:javascript复制
this.setState((state, props) => {
    return {
        var : state.var   1
    }
})

虽然这么写可以让下面读到的state变化,但这仍然是异步的.

setState还有第二个参数.如果写上第二个参数,意为在重新渲染完之后进行的操作.写法例如:

代码语言:javascript复制
this.setState((state, props) => {}, () => {
    console.log('over rendering')
})

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


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

代码语言:javascript复制
const {k, m} = a //此处必须同名,获取之后可以直接使用变量k,m

扩展运算符:对参数对象进行遍历并取出所有可遍历属性,在前面加三个点,类似copy的操作。例如:

代码语言:javascript复制
let bar = { a: 1, b: 2 };
let baz = { c: 3, ...bar }; // { c: 3, a: 1, b: 2 }

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


可控组件

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

代码语言:javascript复制
// 此处默认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的函数并设置为多出口。写法为

代码语言:javascript复制
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里的内容。

父组件调用子组件的信息

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

代码语言:javascript复制
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外边渲染的时候回传通信信息,我们想要对回传的信息进行格式校验,就在外侧写入校验字段。例如:

代码语言:javascript复制
    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

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

代码语言:javascript复制
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装饰器。写法例子:

代码语言:javascript复制
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>. 指定路由出口。代码为

代码语言:javascript复制
<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前缀。代码如下:

代码语言:javascript复制
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 Homeindex.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,插入

代码语言:javascript复制
<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=’’,引号里是服务器匹配的路径。

0 人点赞