<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 減少樣板代碼 Redux 很大部分 [受到 Flux 的啟發](#),并且最常見的關于 Flux 抱怨是它如何使得你寫了一大堆的模板。在這個技巧中,我們將考慮 Redux 如何使得我們選擇我們的代碼會變得怎樣繁復,取決于個人樣式,團隊選項,長期可維護等等。 ### Actions Actions 是描述了在 app 中所發生的,以單獨方式描述對象變異意圖的服務的一個普通對象。很重要的一點是 **你必須分發的 action 對象并不是一個模板,而是 Redux 的一個[基本設計選項](#)**. 有些框架生成自己和 Flux 很像,不過缺少了 action 對象的概念。為了變得可預測,這是一個從 Flux or Redux 的倒退。如果沒有可串行的普通對象 action,便無法記錄或重放用戶會話,或者無法實現 [帶有時間旅行的熱重載](https://www.youtube.com/watch?v=xsSnOQynTHs)。如果你更喜歡直接修改數據,那么你并不需要 Redux 。 Action 一般長這樣: ~~~ { type: 'ADD_TODO', text: 'Use Redux' } { type: 'REMOVE_TODO', id: 42 } { type: 'LOAD_ARTICLE', response: { ... } } ~~~ 一個約定俗成的是 actions 擁有一個定值 type 幫助 reducer (或 Flux 中的 Stores ) 識別它們。我們建議的你使用 string 而不是 [Symbols](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol) 作為 action type ,因為 string 是可串行的,而使用 Symbols 的話你會把記錄和重演變得比所需要的更難。 在 Flux 中,傳統上認為你將每個 action type 定義為string定值: ~~~ const ADD_TODO = 'ADD_TODO'; const REMOVE_TODO = 'REMOVE_TODO'; const LOAD_ARTICLE = 'LOAD_ARTICLE'; ~~~ 這么做的優勢?**人們通常聲稱定值不是必要的,對于小的項目可能是正確的。** 對于大的項目,將action types定義為定值有如下好處: - 幫助維護命名一致性,因為所有的 action type 匯總在同一位置。 - 有的時候,在開發一個新功能之前你想看到所有現存的 actions 。可能的情況是你的團隊里已經有人添加了你所需要的action,而你并不知道。 - Action types 列表在Pull Request中能查到所有添加,刪除,修改的記錄。這能幫助團隊中的所有人及時追蹤新功能的范圍與實現。 - 如果你在導入一個 Action 定值的時候拼寫錯誤,你會得到 `undefined` 。當你納悶 action 被分發出去而什么也沒發生的時候,一個拼寫錯誤更容易被發現。 你的項目的約定取決與你自己。你開始的時候可能用的是inline string,之后轉為定值,也許之后將他們歸為一個獨立文件。Redux 不會給予任何建議,選擇你自己最喜歡的。 ### Action Creators 另一個約定是,你創建生成 action 對象的函數,而不是在你分發的時候內聯生成它們。 例如,用文字對象取代調用 `dispatch` : ~~~ // somewhere in an event handler dispatch({ type: 'ADD_TODO', text: 'Use Redux' }); ~~~ 你可以在單獨的文件中寫一個 action creator ,然后從 component 里導入: #### `actionCreators.js` ~~~ export function addTodo(text) { return { type: 'ADD_TODO', text }; } ~~~ #### `AddTodo.js` ~~~ import { addTodo } from './actionCreators'; // event handler 里的某處 dispatch(addTodo('Use Redux')) ~~~ Action creators 總被當作模板受到批評。好吧,其實你并不用把他們寫出來!**如果你覺得更適合你的項目你可以選用對象文字** 然而,你應該知道寫 action creators 是存在某種優勢的。 假設有個設計師看完我們的原型之后回來說,我們需要允許三個 todo 不能再多了。我們可以使用 [redux-thunk](https://github.com/gaearon/redux-thunk) 中間件添加一個提前退出,把我們的 action creator 重寫成回調形式: ~~~ function addTodoWithoutCheck(text) { return { type: 'ADD_TODO', text }; } export function addTodo(text) { // Redux Thunk 中間件允許這種形式 // 在下面的 “異步 Action Creators” 段落中有寫 return function (dispatch, getState) { if (getState().todos.length === 3) { // 提前退出 return; } dispatch(addTodoWithoutCheck(text)); } } ~~~ 我們剛修改了 `addTodo` action creator 的行為,對調用它的代碼完全不可見。**我們不用擔心去看每個添加 todo 的地方保證他們有了這個檢查** Action creator 讓你可以解耦額外的分發 action 邏輯與實際的 components 發送這些 actions,而且當你在重開發經常要改變需求的時候也會非常有用。 ### 生成 Action Creators 某些框架如 [Flummox](https://github.com/acdlite/flummox) 自動從 action creator 函數定義生成 action type 定值。這個想法是說你不需要 `ADD_TODO` 定值和 `addTodo()` action creator兩個都自己定義。這樣的方法在底層也生成 action type 定值,但他們是隱式生成的,也就是間接級。 我們不建議用這樣的方法。如果你寫像這樣簡單的 action creator 寫煩了: ~~~ export function addTodo(text) { return { type: 'ADD_TODO', text }; } export function removeTodo(id) { return { type: 'REMOVE_TODO', id }; } ~~~ 你可以寫一個生成 action creator 的函數: ~~~ function makeActionCreator(type, ...argNames) { return function(...args) { let action = { type }; argNames.forEach((arg, index) => { action[argNames[index]] = args[index]; }); return action; } } export const addTodo = makeActionCreator('ADD_TODO', 'todo'); export const removeTodo = makeActionCreator('REMOVE_TODO', 'id'); ~~~ 參見 [redux-action-utils](https://github.com/insin/redux-action-utils) 和 [redux-actions](https://github.com/acdlite/redux-actions) 獲得更多介紹這樣的常用工具。 注意這樣的工具給你的代碼添加了魔法。魔法和間接聲明真的值得多寫一兩行代碼么? ### 異步 Action Creators [中間件](#) 讓你注入一個定制邏輯,可以在每個 action 對象分發出去之前解釋。異步 actions 是中間件的最常見用例。 沒有中間件的話,[`dispatch`](#) 只能接收一個普通對象。所以我們在 components 里面進行 AJAX 調用: #### `actionCreators.js` ~~~ export function loadPostsSuccess(userId, response) { return { type: 'LOAD_POSTS_SUCCESS', userId, response }; } export function loadPostsFailure(userId, error) { return { type: 'LOAD_POSTS_FAILURE', userId, error }; } export function loadPostsRequest(userId) { return { type: 'LOAD_POSTS_REQUEST', userId }; } ~~~ #### `UserInfo.js` ~~~ import { Component } from 'react'; import { connect } from 'react-redux'; import { loadPostsRequest, loadPostsSuccess, loadPostsFailure } from './actionCreators'; class Posts extends Component { loadData(userId) { // 調用 React Redux `connect()` 注入 props : let { dispatch, posts } = this.props; if (posts[userId]) { // 這里是被緩存的數據!啥也不做。 return; } // Reducer 可以通過設置 `isFetching` 反應這個 action // 因此讓我們顯示一個 Spinner 控件。 dispatch(loadPostsRequest(userId)); // Reducer 可以通過填寫 `users` 反應這些 actions fetch(`http://myapi.com/users/${userId}/posts`).then( response => dispatch(loadPostsSuccess(userId, response)), error => dispatch(loadPostsFailure(userId, error)) ); } componentDidMount() { this.loadData(this.props.userId); } componentWillReceiveProps(nextProps) { if (nextProps.userId !== this.props.userId) { this.loadData(nextProps.userId); } } render() { if (this.props.isLoading) { return <p>Loading...</p>; } let posts = this.props.posts.map(post => <Post post={post} key={post.id} /> ); return <div>{posts}</div>; } } export default connect(state => ({ posts: state.posts }))(Posts); ~~~ 然而,不久就需要再來一遍,因為不同的 components 從同樣的 API 端點請求數據。而且,我們想要在多個components 中重用一些邏輯(比如,當緩存數據有效的時候提前退出)。 **中間件讓我們寫的更清楚M的潛在的異步 action creators.** 它使得我們分發普通對象之外的東西,并且解釋它們的值。比如,中間件能 “捕捉” 到已經分發的 Promises 并把他們變為一對請求和成功/失敗 actions. 最簡單的中間件例子是 [redux-thunk](https://github.com/gaearon/redux-thunk). **“Thunk” 中間件讓你把 action creators 寫成 “thunks”,也就是返回函數的函數。** 這使得控制被反轉了: 你會像一個參數一樣取得 `dispatch` ,所以你也能寫一個多次分發的 action creator 。 > ##### 注意 > Thunk 只是中間件的一個例子。中間件不是關于 “讓你分發函數” 的:它是關于讓你分發你用的特定中間件知道如何處理的任何東西的。Thunk 中間件添加了一個特定的行為用來分發函數,但這實際上取決于你用的中間件。 考慮上面的代碼用 [redux-thunk](https://github.com/gaearon/redux-thunk) 重寫: #### `actionCreators.js` ~~~ export function loadPosts(userId) { // 用 thunk 中間件解釋: return function (dispatch, getState) { let { posts } = getState(); if (posts[userId]) { // 這里是數據緩存!啥也不做。 return; } dispatch({ type: 'LOAD_POSTS_REQUEST', userId }); // 異步分發原味 actions fetch(`http://myapi.com/users/${userId}/posts`).then( response => dispatch({ type: 'LOAD_POSTS_SUCCESS', userId, respone }), error => dispatch({ type: 'LOAD_POSTS_FAILURE', userId, error }) ); } } ~~~ #### `UserInfo.js` ~~~ import { Component } from 'react'; import { connect } from 'react-redux'; import { loadPosts } from './actionCreators'; class Posts extends Component { componentDidMount() { this.props.dispatch(loadPosts(this.props.userId)); } componentWillReceiveProps(nextProps) { if (nextProps.userId !== this.props.userId) { this.props.dispatch(loadPosts(nextProps.userId)); } } render() { if (this.props.isLoading) { return <p>Loading...</p>; } let posts = this.props.posts.map(post => <Post post={post} key={post.id} /> ); return <div>{posts}</div>; } } export default connect(state => ({ posts: state.posts }))(Posts); ~~~ 這樣打得字少多了!如果你喜歡,你還是可以保留 “原味” action creators 比如從一個 “聰明的” `loadPosts` action creator 里用到的 `loadPostsSuccess` 。 **最后,你可以重寫中間件** 你可以把上面的模式泛化,然后代之以這樣的異步 action creators : ~~~ export function loadPosts(userId) { return { // 要在之前和之后發送的 action types types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'], // 檢查緩存 (可選): shouldCallAPI: (state) => !state.users[userId], // 進行取: callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`), // 在 actions 的開始和結束注入的參數 payload: { userId } }; } ~~~ 解釋這個 actions 的中間件可以像這樣: ~~~ function callAPIMiddleware({ dispatch, getState }) { return function (next) { return function (action) { const { types, callAPI, shouldCallAPI = () => true, payload = {} } = action; if (!types) { // 普通 action:傳走 return next(action); } if ( !Array.isArray(types) || types.length !== 3 || !types.every(type => typeof type === 'string') ) { throw new Error('Expected an array of three string types.'); } if (typeof callAPI !== 'function') { throw new Error('Expected fetch to be a function.'); } if (!shouldCallAPI(getState())) { return; } const [requestType, successType, failureType] = types; dispatch(Object.assign({}, payload, { type: requestType })); return callAPI().then( response => dispatch(Object.assign({}, payload, { response: response, type: successType })), error => dispatch(Object.assign({}, payload, { error: error, type: failureType })) ); }; }; } ~~~ 在傳給 [`applyMiddleware(...middlewares)`](#) 一次以后,你能用相同方式寫你的 API-調用 action creators : ~~~ export function loadPosts(userId) { return { types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'], shouldCallAPI: (state) => !state.users[userId], callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`), payload: { userId } }; } export function loadComments(postId) { return { types: ['LOAD_COMMENTS_REQUEST', 'LOAD_COMMENTS_SUCCESS', 'LOAD_COMMENTS_FAILURE'], shouldCallAPI: (state) => !state.posts[postId], callAPI: () => fetch(`http://myapi.com/posts/${postId}/comments`), payload: { postId } }; } export function addComment(postId, message) { return { types: ['ADD_COMMENT_REQUEST', 'ADD_COMMENT_SUCCESS', 'ADD_COMMENT_FAILURE'], callAPI: () => fetch(`http://myapi.com/posts/${postId}/comments`, { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ message }) }), payload: { postId, message } }; } ~~~ ### Reducers Redux 用函數描述邏輯更新減少了模版里大量的 Flux stores 。函數比對象簡單,比類更簡單得多。 考慮這個 Flux store: ~~~ let _todos = []; export default const TodoStore = assign({}, EventEmitter.prototype, { getAll() { return _todos; } }); AppDispatcher.register(function (action) { switch (action.type) { case ActionTypes.ADD_TODO: let text = action.text.trim(); _todos.push(text); TodoStore.emitChange(); } }); ~~~ 用了 Redux 之后,同樣的邏輯更新可以被寫成 reducing function: ~~~ export function todos(state = [], action) { switch (action.type) { case ActionTypes.ADD_TODO: let text = action.text.trim(); return [...state, text]; default: return state; } } ~~~ `switch` 語句 *不是* 真正的模版。真正的 Flux 模版是概念性的:發送更新的需求,用 Dispatcher 注冊 Store 的需求,Store 是對象的需求 (當你想要一個哪都能跑的 App 的時候復雜度會提升)。 不幸的是很多人仍然靠文檔里用沒用 `switch` 來選擇 Flux 框架。如果你不愛用 `switch` 你可以用一個單獨的函數來解決,下面會演示。 ### 生成 Reducers 讓我們寫一個函數使得我們將 reducers 表達為 action types 到 handlers 的映射對象。例如,在我們的 `todos` reducer 里這樣定義: ~~~ export const todos = createReducer([], { [ActionTypes.ADD_TODO](state, action) { let text = action.text.trim(); return [...state, text]; } } ~~~ 我們可以寫下面的幫忙函數來完成: ~~~ function createReducer(initialState, handlers) { return function reducer(state = initialState, action) { if (handlers.hasOwnProperty(action.type)) { return handlers[action.type](state, action); } else { return state; } } } ~~~ 不難對吧?Redux 沒有默認提供這樣的幫忙函數,因為有好多種寫的方法。可能你想要自動把普通 JS 對象變成不可變對象通過濕化服務器狀態。可能你想合并返回狀態和當前狀態。有很多方法 “獲取所有” handler。這些都取決于你為你的團隊在特定項目中選擇的約定。 Redux reducer 的 API 是 `(state, action) => state`,但是怎么創建這些 reducers 由你來定。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看