React 項目開發中的常見問題


 

React 中, 在 Controlled(受控制)的文本框中輸入中文 onChange 會觸發多次

Issues

通過輸入法輸入中文,日文,不管哪種一種用拼音的、筆划的,只要按下鍵盤的動作都會觸發文本框的 change 事件。 但是 React中的受控組件都通過觸發 change 事件后得到的值再去更新組件的value, 這樣不停的觸發 change,導致 value 一直在更新,組件就一直在 render, 可能會造成組件運行邏輯問題,輸入框內出現很多字符,而且中文輸入不進去。

在 DOM events 中另外有 3 個事件可以輔助監聽一段文字的輸入:

  • compositionstart : 事件觸發於一段文字的輸入之前;
  • compositionupdate : 事件觸發於字符被輸入到一段文字的時候;
  • compositionend : 事件觸發於一段文字的輸入完成。

我們可以通過這 3 個事件判斷當前輸入是否完整的輸入,再觸發 onChange 事件,這樣就能解決多次觸發 onChange 的問題。 還有一個重要的問題,雖然 onChange 要等一次完整輸入后才觸發,但是文本框上的顯示需要是實時的,所以還要考慮在 state 中維護一個 innerValue 做實時更新,這樣就沒有問題了。

另外這 3 個事件在各個瀏覽器上的兼容性不一樣,所以還要考慮兼容性的問題。 具體詳細的實現可以參考 form-lib

Warning: It looks like you’re using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster.

當 React 升級到 v15.* 以后,可能會見到這個警告,它是意思說你在生產環境中使用的是一個開發環境 minified 的代碼。

那 React 它是怎么辦判斷你正在訪問的是生產環境,而不是開發環境呢? 它是通過 webpack 知道的,因為最終發布生產環境的代碼都是通過 webpack 命令打包。如果你是在開發環境,那肯定是用 webpack-dev-server。 在生產環境中不應該包括開發中使用的所有額外代碼,所以在 webpack 部署環境中需要添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
//...
plugins:[
new webpack.DefinePlugin({
'process.env':{
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings: true
}
})
]
//...
}

參考Make your own React production version with webpack

setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.

這個警告是說,在一個已經卸載的組件上調用 setState 是無效的。出現這種情況一般都是異步操作造成的,比如一個異步數據請求,響應后執行 setState 去更新組件,但是如果在異步請求未完成前,路由發生變化,頁面當前組件被卸載,這個時候異步請求沒有被中止,導致等請求完成后就會執行 setState 試圖去更新組件,就會報當前錯誤。

解決辦法很簡單,一般都是在執行 setState 之前判斷一下當前組件是否被卸載,如果沒有被卸載,才執行 setState。在 ES6 以前可以通過 this.isMounted() 來判斷當前組件是否裝載,如下:

1
2
3
if(this.isMounted()) { // This is bad.
this.setState({...});
}

但是在 ES6 以后不能用這種方式,這是一種反模式,參考isMounted is an Antipattern

可以利用組件的生命周期自己維護一個 isMounted 狀態, 在 componentDidMount 中把 isMounted 設置為 true,
在 componentWillUnmount 再將 isMounted 設置為 false。 通過 isMounted 變量來判斷組件是否裝載。

Each child in an array or iterator should have a unique key prop.

這個警告很明確,在遍歷子元素的時候,每一個子元素都應該有一個唯一的 key

參考官網相關說明 lists and keys

但是這里需要注意,避免使用數組的 index 來作為屬性 key 的值,推薦使用唯一 ID。
參考 Index as a key is an anti-pattern

1
2
3
4
5
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);

在 IE 11 控制台報錯:Objects are not valid as a React child

錯誤的全文: error-decoder

這問題一般會在開發環境中遇到,在使用 React 15.4 以后,如果使用了 react-hot-loader 則必須在熱加載之前加載 babel-polyfill, 在你的 webpack.config.js 中參考如下配置:

1
2
3
4
5
6
7
entry: [
'babel-polyfill',
'react-hot-loader/patch',
'webpack-dev-server/client?http://127.0.0.1:3000',
'webpack/hot/only-dev-server',
path.resolve(__dirname, './scr/index')
]

在 IE 11 控制台報錯:Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.

如果出現這個錯誤是提示,在本地環境配置 NODE_ENV=development ,再看一下控制台是否輸出了一些更詳細的錯誤信息,參考 Introducing React’s Error Code System 。

如果這個錯誤只在 IE 11 瀏覽器環境出現,一般都是 ES6+ 的語法在IE存在兼容問題,一個比較通用的解決辦法就是在項目中引入一個 babel-polyfill

1
npm i —save babel-polyfill

在項目入口,引入 babel-polyfill

1
import 'babel-polyfill'

在 webpack 構建的時候會兼容性處理

在 IE 11 控制台報錯: Promise is undefined

IE 11 不支持 Promise 對象,解決辦法

1
npm i —save es6-promise

在項目入口,引入es6-promise

1
import 'es6-promise/auto';

在 IE 11 瀏覽器上 FontIcon 圖標不顯示

這個問題和 React 沒有關系,但是這里也記錄一下。
在 IE11 會下載 .ttf/.woff 字體文件, 通過 Network 我們可以看到字體文件 response headers 中有一個 Pragma:no-cache,由於 IE 似乎有緩存和字體的問題,所有導致圖標不能正常顯示。所以刪除 WEB 服務(Nginx..)中的 Pragma:no-cache 和 Cache-Control:no-store 就能正常訪問。

參考 IE and Cache-Control

在 React 中使用 debounce

如果需要在 React 組件中使用 debounce 方法可以參考下面代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.method = debounce(this.method,300);
}
method() {
//...
}
render(){
return(
<input onKeyUp={e => {
e.persist();
this.handleKeyDown(e);
}}/>
)
}
}

 

因為 React 中 SyntheticEvent 是共用的,也就是說 SyntheticEvent對象將會循環使用。而每次執行完事件后,它所有的屬性都將會失效(所有屬性的值都被置為了 null)。所以在調用 debounce 方法前,一定要先調用 e.persist()來移除 SyntheticEvent ,並允許 event對象被保留下來以用於你自己的代碼。
參考文章:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM