react使用过程中常见问题

react

目录

一、减小输入字符数
二、用props.children来引用位于前置标签和后置标签之间的内容


三、创建组件两条主要的途径


四、JSX属性采用驼峰式的大小写规则(即‘onClick’而非‘onclick’)


五、JSX只能渲染单一个根节点


六、JSX中不方便使用条件语句的解决方法


七、如何在JSX内部渲染HTML标签


八、列表子元素添加key可以提升virtual dom的子级校正(reconciliation)的速度


九、JSX内联样式采用驼峰式大小写规则,以保持和DOM属性一致


十、高阶组件的主要作用


十一、为什么要用immutable.js?


十二、window.fetch的浏览器兼容补丁 whatwg-fetch


十三、js ES6新函数的兼容浏览器垫片, babel-polyfill


十四、Redux-Thunk的作用是改造store.dispatch函数,让其可以接收函数进行分发,函数可带上(dispatch,getState)两参数进行传递


十五、如何编写redux自定义middleware中间件


十六、FSA(Flux Standard Action)结构的Action


十七、redux-saga的redux-saga/effects常用的指令


十八、redux使用过程中常用的lodash函数


十九、redux常用的驼峰化处理库humps


二十、redux常用的json格式化处理库normalizr


二十一、react中常用到的新的js函数


二十二、react中如何阻止事件冒泡


二十三、Create React App中的代码切割,Code Splitting in Create React App


二十四、React组件生命周期


二十五、setState注意事项


二十六、mobx在ReactJS项目中的运用


二十七、报Error: Plugin/Preset files are not allowed to export objects, only functions.错误的解决方案

一、减小输入字符数

代码如下:

import React, { Component } from 'react';

class Hello extends Component {

// ...

}  

二、用props.children来引用位于前置标签和后置标签之间的内容

代码如下:

import React, { Component } from 'react';

import { render } from 'react-dom';

// Parent Component

class GroceryList extends Component {

render() {

return (

<ul>

<ListItem quantity = "1" > Bread </ListItem>

<ListItem quantity = "6" > Eggs </ListItem>

<ListItem quantity = "2" > Milk </ListItem>

</ul>

);

}

}

// Child Component

class ListItem extends Component {

render() {

return (<li> {this.props.quantity}× {this.props.children} </li>);

}

}

render(<GroceryList /> , document.getElementById('root'));

三、创建组件两条主要的途径

 从上而下或者从下而上,尽量让app.js保持简单,只包含数据(数据来自于API)并只渲染出一个KanbanBoard组件

import React from 'react';

import { render } from 'react-dom';

import KanbanBoard from './KanbanBoard';

let cardsList = [

{ id: 1, title: "Read the Book", description: "I should read the whole book", status: "in-progress", tasks: [] }, {

id: 2,

title: "Write some code",

description: "Code along with the samples in the book",

status: "todo",

tasks: [

{ id: 1, name: "ContactList Example", done: true },

{ id: 2, name: "Kanban Example", done: false },

{ id: 3, name: "My own experiments", done: false }

]

}

];

render( <KanbanBoard cards = { cardsList } />, document.getElementById('root'));

四、react属性采用驼峰式的大小写规则(即‘onClick’而非‘onclick’)

五、react只能渲染单一个根节点

代码如下:

return (

<h1>Hello World</h1>

) // 合法

return (

<h1>Hello World</h1>

<h2>Have a nice day</h2>

) // 不合法

六、JSX中不方便使用条件语句的解决方法

 解决方案一、使用三元表达式

render(){

return (

<div className={condition ? "show":"hide"}>

Hello JSX

</div>

)

}

// 或者

<div>

{condition?

<span> Hello JSX </span>

: null}

</div>

 解决方案二、将条件外置

render(){

let className;

if(condition) {

className = "show";

} else {

className = "hide";

}

}

return (

<div className={className}>Hello JSX</div>

)

七、如何在JSX内部渲染HTML标签

方式、1

var HelloMessge = React.createClass({

render: <div

dangerouslySetInnerHTML={{

__html: '<h3>hahhah</h3>'

}}>

</div>

})

方式、2

  destroy() {

if (this._el) {

this._el.querySelector('.dialog__mask').classList.add('maskFadeOut')

this._el.querySelector('.dialog__wrapper').classList.add('wrapperFadeOutUp')

setTimeout(()=>{

ReactDOM.unmountComponentAtNode(this._el)

document.body.removeChild(this._el)

this._el = null

}, 150)

}

}

open() {

this._el = document.createElement('div')

document.body.appendChild(this._el)

ReactDOM.unstable_renderSubtreeIntoContainer(

this,

this.renderDialog(),

this._el

); // 更新组件到传入的 DOM 节点上,完成在组件内实现跨组件的 DOM 操作

}

renderDialog() {

const {

skin,

width,

okBtn,

okBtnText,

children,

cancelBtn,

cancelBtnText

} = this.props;

return (

<div className="dialog" key="dialog">

<div className="dialog__mask maskFadeIn dialog_animated" style={{height: (document.body.offsetHeight > window.screen.height ? document.body.offsetHeight : window.screen.height) + 'px'}} />

<div className={'dialog__wrapper wrapperFadeInDown dialog_animated dialog__wrapper--skin-' + skin} style={{left:'50%', top: (window.screen.height/2 - 100) + 'px', width: width + 'px', marginLeft: (width*(-1)/2) + 'px'}} >

<div className="dialog__content">

{children}

</div>

{(okBtn || cancelBtn) && (

<div className="dialog__btns">

{okBtn && (<button className="dialog__btn dialog__btn--ok" onClick={this.onOk}>{okBtnText}</button>)}

{cancelBtn && <button className="dialog__btn dialog__btn--cancel" onClick={this.onCancel}>{cancelBtnText}</button>}

</div>

)}

</div>

</div>

)

}

  

八、列表子元素添加key可以提升virtual dom的子级校正(reconciliation)的速度

九、JSX内联样式采用驼峰式大小写规则,以保持和DOM属性一致  

十、高阶组件的主要作用

一:生成包含新功能的新组件

function hoc(Comp){

return class NewComponent extends Component {

// 增加或者修改的功能实现

extendFunc(){

}

render() {

return (

<Comp {... this.props} />

)

}

}

}

const NewCompent = hoc(Comp);

二:控制props

const MyContainer = (WrappedComponent) =>

class extends Component {

render() {

const newProps = { text: newText };

return <WrappedComponent {...this.props} {...newProps} />;

}

}

属性转换

function transProps(transFn){

return function(Comp){

return class extends Component {

render(){

return <Comp {...transFn(this.props)} />

}

}

}

}

三:抽象state,高阶组件可以将原组件抽象为展示型组件,分离内部状态

const MyContainer = (WrappedComponent) =>

class extends Component {

constructor(props){

super(props);

this.state = {

name: '',

};

this.onNameChange = this.onNameChange.bind(this);

}

onNameChange( event ) {

this.setState(

name: event.target.value,

)

}

render() {

const newPros = {

name: {

value: this.state.name,

onChange: this.onNameChange,

}

}

return <WrappedComponent {...this.props} {...newProps} />;

}

}

四:封装异步请求

function hocListWithData({dataLoader, getListFromResultData}) {

return Com => {

return class extends Component {

constructor(props){

super();

this.state = {

resultData: undefined

}

}

componentDidMount(){

dataLoader()

.then(data=> this.setState({resultData: data})

}

render(){

return {

<Comp {...getListFromResultData(this.state.resultData)} {...this.props} />

}

}

}

}

}

十一、为什么要用immutable.js?

举个例子,在javascript中

var a = {a:1};

var b = a;

b.a = 2; // => a.a = 2

由上面的例子可以知道,可变数据有时使用起来是会有问题的,在各种数据操作后有可能会使原数据被污染而导致程序出错,才出现immutable.js不可修改数据类型的概念,因此,修改组件状态时,永远不能直接写:

this.state.arr = newArr; // 或者 

const tempArr = this.state.arr;

temp.push('hello'); // 此时已经修改了this.state了,不能这样写

this.state.setState({newArr,tempArr })

可以使用非常侵入式的纯函数如:map、filter、concat或者Object.assign({}, this.state.xxx, {newKey, newValue})来解决这个问题

immutable例子如下:

import React from 'react'

import { Map, Set } from 'immutable';

export default class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {

immutableData: Map({

date: new Date(),

name: 'Nelson won\'t change, but time is changing all the time!'

})

};

this.dataSet = Set();

}

componentDidMount() {

this.timerID = setInterval(

() => this.tick(),

10

);

let dataSet = Set();

dataSet = dataSet.add(1);

dataSet = dataSet.add(2);

this.dataSet = dataSet;

}

componentWillUnmount() {

clearInterval(this.timerID);

}

tick() {

this.setImmutableState('date',new Date());

}

setImmutableState(key, value) {

this.setState({

immutableData: this.state.immutableData.set(key, value)

});

}

render() {

const formatTime = date => {

let hour = date.getHours();

let minute = date.getMinutes();

let second = date.getSeconds();

let milisecond = Math.floor(date.getMilliseconds() / 10);

if (hour < 10) {

hour = '0' + hour;

}

if (minute < 10) {

minute = '0' + minute;

}

if (second < 10) {

second = '0' + second;

}

if (milisecond < 10) {

milisecond = '0' + milisecond;

}

return `${hour} : ${minute} : ${second} : ${milisecond}`;

}

return (

<div>

<h2>现在时间 {formatTime(this.state.immutableData.get('date'))}. Hello {this.state.immutableData.get('name')}. dataSetSize:{this.dataSet.size}</h2>

</div>

);

}

}

十二、window.fetch的浏览器兼容补丁 whatwg-fetch

npm install --save whatwg-fetch

import 'whatwg-fetch';

十三、js ES6新函数的兼容浏览器垫片, babel-polyfill

npm install --save babel-polyfill

十四、Redux-Thunk的作用是改造store.dispatch函数,让其可以接收函数进行分发,函数可带上(dispatch,getState)两参数进行传递解决异步action传递的问题。

如下面action代码的写法,组件中可以直接写 dispatch(fetchPostsIfNeeded) 分发异步 action:

export const requestPosts = reddit => ({

type: REQUEST_POSTS,

reddit

})

export const receivePosts = (reddit, json) => ({

type: RECEIVE_POSTS,

reddit,

posts: json.data.children.map(child => child.data),

receivedAt: Date.now()

})

const fetchPosts = reddit => dispatch => {

dispatch(requestPosts(reddit))

return fetch(`https://www.reddit.com/r/${reddit}.json`)

.then(response => response.json())

.then(json => dispatch(receivePosts(reddit, json)))

}

const shouldFetchPosts = (state, reddit) => {

const posts = state.postsByReddit[reddit]

if (!posts) {

return true

}

if (posts.isFetching) {

return false

}

return posts.didInvalidate

}

export const fetchPostsIfNeeded = reddit => (dispatch, getState) => {

if (shouldFetchPosts(getState(), reddit)) {

return dispatch(fetchPosts(reddit))

}

}

十五、如何编写redux自定义middleware中间件

自定义的middleware一般可以用做处理复杂的异步流,除了redux-thunk, redux-saga这些非常常用的中间件,我们可以自己定义一些中间件:

如处理轮询、多异步串联、修改并重新封装action的值等,

一般的写法如下:

// 如多异步串联

const sequenceMiddleware = {dispatch, getState} => next => action => {

  if(!Array.isArray(action)){

    return next(action);

}

  return action.reduce((result, currAction) => {

    return result.then() => {

     return Array.isArray(currAction) ?

      Promise.all(currAction.map(item => dispatch(item))) :

        dispatch(currAction);

    })

  }, Promise.resolve());

}

// 或者这样写

export default store => next => action => {

  // ...

}

代码引用自:《深入react技术栈》  

十六、FSA(Flux Standard Action)结构的Action

{

type: 'ADD_TODO',

payload: {

text: 'Do something.'

}

}

// 一个action必须是一个普通的JavaScript对象,有一个type字段

// 一个action可能有error字段、payload字段、meta字段。

// 一个action必须不能包含除type、payload、error及meta以外的其他字段。  

十七、redux-saga的redux-saga/effects常用的指令

yield put({ type: 'INCREMENT' }) // put: 分发一个type为'INCREMENT'的action 到 Store

yield call(delay, 1000) // 不直接执行delay函数,用call便于跟踪及测试

yield call([obj, obj.method], arg1, arg2, ...) // 如同 obj.method(arg1, arg2 ...)

yield apply(obj, obj.method, [arg1, arg2, ...]) // call 和 apply 非常适合返回 Promise 结果的函数

const content = yield cps(readFile, '/path/to/file') // cps 可以用来处理 Node 风格的函数 (例如,fn(...args, callback) 中的 callback 是 (error, result) => () 这样的形式,cps 表示的是延续传递风格(Continuation Passing Style))

yield* takeEvery('FETCH_REQUESTED', fetchData) // takeEvery 监听并允许多个 fetchData 实例同时启动

yield* takeLatest('FETCH_REQUESTED', fetchData) // takeLatest 监听并只允许执行一个 fetchData 任务。并且这个任务是最后被启动的那个。 如果之前已经有一个任务在执行,那之前的这个任务会自动被取消

// 错误处理

function* fetchProducts() {

try {

const products = yield call(Api.fetch, '/products')

yield put({ type: 'PRODUCTS_RECEIVED', products })

}

catch(error) {

yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })

}

}

// 简单的logger

export function* watchAndLog() {

while (true) {

const action = yield take('*') // 监听所有action

const state = yield select() // select作用和 redux thunk 中的 getState 相同

console.log('action', action)

console.log('state after', state)

}

}

// select内容

const id = yield select(state => state.id);

const tokenTask= yield fork(authorize, user, password) // 相比起call来说,fork是无阻塞调用

yield cancel(tokenTask) // 可取消task

// 正确写法, effects 将会同步并行执行

const [users, repos] = yield [

call(fetch, '/users'),

call(fetch, '/repos')

]

// 当我们需要 yield 一个包含 effects 的数组, generator 会被阻塞直到所有的 effects 都执行完毕,或者当一个 effect 被拒绝 (就像 Promise.all 的行为)。

const {posts, timeout} = yield race({

posts : call(fetchApi, '/posts'),

timeout : call(delay, 1000)

}) // race Effect 提供了一个方法,在多个 Effects 之间触发一个竞赛(race)。它会自动取消那些失败的 Effects

// yield* 对 Sagas 进行排序,可以使用内置的 yield* 操作符来组合多个 Sagas,使得它们保持顺序,如下:

function* playLevelOne(getState) { /*...*/ }

function* playLevelTwo(getState) { /*...*/ }

function* playLevelThree(getState) { /*...*/ }

function* game(getState) {

const score1 = yield* playLevelOne(getState)

put(showScore(score1))

const score2 = yield* playLevelTwo(getState)

put(showScore(score2))

const score3 = yield* playLevelThree(getState)

put(showScore(score3))

}

// 任务的取消

// 或直接使用 `isCancelError(error)`

if(error instanceof SagaCancellationException)

yield put(actions.requestFailure('Sync cancelled!'))

十八、redux使用过程中常用的lodash函数

_.union([1, 2], [4, 2], [2, 1]); // 字符或者数字数组去重

// => [1, 2, 4]

var users = {

'data': [{ 'user': 'barney' }, { 'user': 'fred' }]

};

var ages = {

'data': [{ 'age': 36 }, { 'age': 40 }]

};

_.merge(users, ages); // 合并对象数组

// => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }

// using a customizer callback

var object = {

'fruits': ['apple'],

'vegetables': ['beet']

};

var other = {

'fruits': ['banana'],

'vegetables': ['carrot']

};

_.merge(object, other, function(a, b) { // 按函数合并对象数组

if (_.isArray(a)) {

return a.concat(b);

}

});

// => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }

_.zip(['fred', 'barney'], [30, 40], [true, false]); // 数组按顺序组合成新数组

// => [['fred', 30, true], ['barney', 40, false]]

十九、redux常用的驼峰化处理库humps

humps.camelize('hello_world') // 'helloWorld'

humps.decamelize('fooBar') // 'foo_bar'

humps.decamelize('fooBarBaz', { separator: '-' }) // 'foo-bar-baz'

var object = { attr_one: 'foo', attr_two: 'bar' }

humps.camelizeKeys(object); // { attrOne: 'foo', attrTwo: 'bar' }

var array = [{ attr_one: 'foo' }, { attr_one: 'bar' }]

humps.camelizeKeys(array); // [{ attrOne: 'foo' }, { attrOne: 'bar' }]

二十、redux常用的json格式化处理库normalizr

// 源数据

{

"id": "123",

"author": {

"id": "1",

"name": "Paul"

},

"title": "My awesome blog post",

"comments": [

{

"id": "324",

"commenter": {

"id": "2",

"name": "Nicole"

}

}

]

}

// 处理

import { normalize, schema } from 'normalizr';

// Define a users schema

const user = new schema.Entity('users');

// Define your comments schema

const comment = new schema.Entity('comments', {

commenter: user

});

// Define your article

const article = new schema.Entity('articles', {

author: user,

comments: [ comment ]

});

const normalizedData = normalize(originalData, article);

// =>

{

result: "123",

entities: {

"articles": {

"123": {

id: "123",

author: "1",

title: "My awesome blog post",

comments: [ "324" ]

}

},

"users": {

"1": { "id": "1", "name": "Paul" },

"2": { "id": "2", "name": "Nicole" }

},

"comments": {

"324": { id: "324", "commenter": "2" }

}

}

}

二十一、react中常用到的新的js函数

const cart = json.carts.find(cart => cart.cartId === id);

Object.assign({}, obj, obj);

// reduce

// map

// filter ...还有 ... spread函数

二十二、react中如何阻止事件冒泡

        onDelete={(e) => {

e.stopPropagation();

e.nativeEvent.stopImmediatePropagation();

onDelete(todo.id)

}

}

二十三、Create React App中的代码切割,Code Splitting in Create React App

参考:Code Splitting in Create React App

二十四、React组件生命周期

参考:React Component Lifecycle(生命周期)和 React组件生命周期小结

二十五、setState注意事项

1、setState是异步的。
2、setState会造成不必要的渲染,新的 state 其实和之前的有可能是一样的。这个问题通常可以通过 shouldComponentUpdate 来解决。也可以用 pure render 或者其他的库赖解决这个问题。
3、setState并不能很有效的管理所有的组件状态

二十六、mobx在ReactJS项目中的运用

引用自:http://blog.csdn.net/u012125579/article/details/69400169

mobx 最最核心的概念只有2个。 @observable 和 @observer ,它们分别对应的是被观察者和观察者。这是大家常见的观察者模式,不过这里使用了,ES7 中的 装饰器。

核心概念2 actions 和 computed values,在 Component 中调用,这样通过 action 的方法,就避免了直接修改 props 的问题。可以通过引入 mobx 定义的严格模式,强制使用 action 来修改状态。mobx 提供了 computed 装饰器,用于获取由基础 state 衍生出来的值

import React, {Component} from 'react';

import { render } from 'react-dom';

import {observable, action, computed,useStrict} from 'mobx';

import {observer} from 'mobx-react';

useStrict(true);

class Store {

@observable todos = [{ // 被观察者

title: "todo标题",

done: false,

},{

title: "已经完成 todo 的标题",

done: true,

}];

@action changeTodoTitle({index,title}){

this.todos[index].title = title

}

@computed get unfinishedTodos () {

return this.todos.filter((todo) => todo.done)

}

}

@observer // 观察者

class TodoBox extends Component {

render() {

console.log('render');

return (

<div>

<ul>

{ /* 把 unfinishedTodos 换成 todos,点击修改标题就会在控制台打印 "render".*/ }

{this.props.store.unfinishedTodos.map(

(todo,index) => <li key={index}>{todo.title}</li>

)}

</ul>

<div>

<input type="button" onClick={() => {

this.props.store.changeTodoTitle({index:0,title:"修改后的todo标题"});

}} value="修改标题"/>

</div>

</div>

)

}

}

const store = new Store();

render(

<TodoBox store={store} />,

document.getElementById('root')

);

二十七、报Error: Plugin/Preset files are not allowed to export objects, only functions.错误的解决方案

原因是 package.json 依赖包中既有 babel 7.0 版本,又有 babel 6.0 版本,就会报这个错误

解决方案:要么全部bebel相关的包升级到7.0,要么全部降级到6.0版的,再不行检查一下全部安装的babel版本,删除node_modules重新装包

以上是 react使用过程中常见问题 的全部内容, 来源链接: utcz.com/z/382007.html

回到顶部