| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | id: 5a24c314108439a4d4036144 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | title: 使用 Provider 连接 Redux 和 React | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | challengeType: 6 | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  | forumTopicId: 301435 | 
					
						
							| 
									
										
										
										
											2021-01-13 03:31:00 +01:00
										 |  |  |  | dashedName: use-provider-to-connect-redux-to-react | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | --- | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | # --description--
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | 在上一挑战中,创建了 Redux store 和 action,分别用于处理消息数组和添加新消息。 下一步要为 React 提供访问 Redux store 及发起更新所需的 actions。 `react-redux` 包可帮助我们完成这些任务。 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | React Redux 提供的 API 有两个关键的功能:`Provider` 和 `connect`。 会在另一个挑战会介绍 `connect`。 `Provider`是 React Redux 包装 React 应用的 wrapper 组件, 它允许访问整个组件树中的 Redux `store` 及 `dispatch`(分发)方法。 `Provider` 需要两个 props:Redux store 和 App 应用的子组件。 用于 App 组件的 `Provider` 可这样定义: | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```jsx | 
					
						
							|  |  |  |  | <Provider store={store}> | 
					
						
							|  |  |  |  |   <App/> | 
					
						
							|  |  |  |  | </Provider> | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | # --instructions--
 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | 此时,编辑器上显示的是过去几个挑战中所有代码, 包括 Redux store、actions、`DisplayMessages` 组件。 新出现的代码是底部的`AppWrapper`组件, 这个顶级组件可用于渲染 `ReactRedux` 的 `Provider`,并把 Redux 的 store 作为 props 传入。 接着,渲染 `DisplayMessages` 为子组件。 完成这些任务后,会看到 React 组件渲染到页面上。 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | **注意:** React Redux 在此可作全局变量,因此可通过点号表示法访问 Provider。 利用这一点,编辑器上的代码把 `Provider` 设置为常量,便于你在 `AppWrapper` 渲染方法中使用。 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | # --hints--
 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | `AppWrapper` 应渲染。 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | ```js | 
					
						
							|  |  |  |  | assert( | 
					
						
							|  |  |  |  |   (function () { | 
					
						
							|  |  |  |  |     const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); | 
					
						
							|  |  |  |  |     return mockedComponent.find('AppWrapper').length === 1; | 
					
						
							|  |  |  |  |   })() | 
					
						
							|  |  |  |  | ); | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | `Provider` 组件应传入相当于 Redux store 的 `store` 参数。 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```js | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | (getUserInput) => | 
					
						
							|  |  |  |  |   assert( | 
					
						
							|  |  |  |  |     (function () { | 
					
						
							|  |  |  |  |       const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); | 
					
						
							| 
									
										
										
										
											2021-02-06 04:42:36 +00:00
										 |  |  |  |       return __helpers | 
					
						
							|  |  |  |  |         .removeWhiteSpace(getUserInput('index')) | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  |         .includes('<Providerstore={store}>'); | 
					
						
							|  |  |  |  |     })() | 
					
						
							|  |  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | `DisplayMessages` 应渲染为 `AppWrapper` 的子组件。 | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 18:03:03 -04:00
										 |  |  |  | ```js | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | assert( | 
					
						
							|  |  |  |  |   (function () { | 
					
						
							|  |  |  |  |     const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  |       mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1 | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  |   })() | 
					
						
							|  |  |  |  | ); | 
					
						
							|  |  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2020-08-13 17:24:35 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 08:43:24 -07:00
										 |  |  |  | `DisplayMessages` 组件应渲染 `h2`、`input`、`button`、`ul` 四个元素。 | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | ```js | 
					
						
							|  |  |  |  | assert( | 
					
						
							|  |  |  |  |   (function () { | 
					
						
							|  |  |  |  |     const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  |       mockedComponent.find('div').length === 1 && | 
					
						
							|  |  |  |  |       mockedComponent.find('h2').length === 1 && | 
					
						
							|  |  |  |  |       mockedComponent.find('button').length === 1 && | 
					
						
							|  |  |  |  |       mockedComponent.find('ul').length === 1 | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  |   })() | 
					
						
							|  |  |  |  | ); | 
					
						
							| 
									
										
										
										
											2020-09-07 16:11:48 +08:00
										 |  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-13 03:31:00 +01:00
										 |  |  |  | # --seed--
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## --after-user-code--
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```jsx | 
					
						
							|  |  |  |  | ReactDOM.render(<AppWrapper />, document.getElementById('root')) | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## --seed-contents--
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ```jsx | 
					
						
							|  |  |  |  | // Redux: | 
					
						
							|  |  |  |  | const ADD = 'ADD'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const addMessage = (message) => { | 
					
						
							|  |  |  |  |   return { | 
					
						
							|  |  |  |  |     type: ADD, | 
					
						
							|  |  |  |  |     message | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const messageReducer = (state = [], action) => { | 
					
						
							|  |  |  |  |   switch (action.type) { | 
					
						
							|  |  |  |  |     case ADD: | 
					
						
							|  |  |  |  |       return [ | 
					
						
							|  |  |  |  |         ...state, | 
					
						
							|  |  |  |  |         action.message | 
					
						
							|  |  |  |  |       ]; | 
					
						
							|  |  |  |  |     default: | 
					
						
							|  |  |  |  |       return state; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const store = Redux.createStore(messageReducer); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // React: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class DisplayMessages extends React.Component { | 
					
						
							|  |  |  |  |   constructor(props) { | 
					
						
							|  |  |  |  |     super(props); | 
					
						
							|  |  |  |  |     this.state = { | 
					
						
							|  |  |  |  |       input: '', | 
					
						
							|  |  |  |  |       messages: [] | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     this.handleChange = this.handleChange.bind(this); | 
					
						
							|  |  |  |  |     this.submitMessage = this.submitMessage.bind(this); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   handleChange(event) { | 
					
						
							|  |  |  |  |     this.setState({ | 
					
						
							|  |  |  |  |       input: event.target.value | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   submitMessage() {   | 
					
						
							|  |  |  |  |     this.setState((state) => { | 
					
						
							|  |  |  |  |       const currentMessage = state.input; | 
					
						
							|  |  |  |  |       return { | 
					
						
							|  |  |  |  |         input: '', | 
					
						
							|  |  |  |  |         messages: state.messages.concat(currentMessage) | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   render() { | 
					
						
							|  |  |  |  |     return ( | 
					
						
							|  |  |  |  |       <div> | 
					
						
							|  |  |  |  |         <h2>Type in a new Message:</h2> | 
					
						
							|  |  |  |  |         <input | 
					
						
							|  |  |  |  |           value={this.state.input} | 
					
						
							|  |  |  |  |           onChange={this.handleChange}/><br/> | 
					
						
							|  |  |  |  |         <button onClick={this.submitMessage}>Submit</button> | 
					
						
							|  |  |  |  |         <ul> | 
					
						
							|  |  |  |  |           {this.state.messages.map( (message, idx) => { | 
					
						
							|  |  |  |  |               return ( | 
					
						
							|  |  |  |  |                  <li key={idx}>{message}</li> | 
					
						
							|  |  |  |  |               ) | 
					
						
							|  |  |  |  |             }) | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         </ul> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const Provider = ReactRedux.Provider; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class AppWrapper extends React.Component { | 
					
						
							|  |  |  |  |   // Render the Provider below this line | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // Change code above this line | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 00:37:30 -07:00
										 |  |  |  | # --solutions--
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-13 03:31:00 +01:00
										 |  |  |  | ```jsx | 
					
						
							|  |  |  |  | // Redux: | 
					
						
							|  |  |  |  | const ADD = 'ADD'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const addMessage = (message) => { | 
					
						
							|  |  |  |  |   return { | 
					
						
							|  |  |  |  |     type: ADD, | 
					
						
							|  |  |  |  |     message | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const messageReducer = (state = [], action) => { | 
					
						
							|  |  |  |  |   switch (action.type) { | 
					
						
							|  |  |  |  |     case ADD: | 
					
						
							|  |  |  |  |       return [ | 
					
						
							|  |  |  |  |         ...state, | 
					
						
							|  |  |  |  |         action.message | 
					
						
							|  |  |  |  |       ]; | 
					
						
							|  |  |  |  |     default: | 
					
						
							|  |  |  |  |       return state; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const store = Redux.createStore(messageReducer); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // React: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class DisplayMessages extends React.Component { | 
					
						
							|  |  |  |  |   constructor(props) { | 
					
						
							|  |  |  |  |     super(props); | 
					
						
							|  |  |  |  |     this.state = { | 
					
						
							|  |  |  |  |       input: '', | 
					
						
							|  |  |  |  |       messages: [] | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |  this.handleChange = this.handleChange.bind(this); | 
					
						
							|  |  |  |  |  this.submitMessage = this.submitMessage.bind(this); | 
					
						
							|  |  |  |  |  } | 
					
						
							|  |  |  |  |   handleChange(event) { | 
					
						
							|  |  |  |  |     this.setState({ | 
					
						
							|  |  |  |  |       input: event.target.value | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   submitMessage() { | 
					
						
							|  |  |  |  |     this.setState((state) => { | 
					
						
							|  |  |  |  |       const currentMessage = state.input; | 
					
						
							|  |  |  |  |       return { | 
					
						
							|  |  |  |  |         input: '', | 
					
						
							|  |  |  |  |         messages: state.messages.concat(currentMessage) | 
					
						
							|  |  |  |  |       };   | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   render() { | 
					
						
							|  |  |  |  |     return ( | 
					
						
							|  |  |  |  |       <div> | 
					
						
							|  |  |  |  |         <h2>Type in a new Message:</h2> | 
					
						
							|  |  |  |  |         <input | 
					
						
							|  |  |  |  |           value={this.state.input} | 
					
						
							|  |  |  |  |           onChange={this.handleChange}/><br/> | 
					
						
							|  |  |  |  |         <button onClick={this.submitMessage}>Submit</button> | 
					
						
							|  |  |  |  |         <ul> | 
					
						
							|  |  |  |  |           {this.state.messages.map( (message, idx) => { | 
					
						
							|  |  |  |  |               return ( | 
					
						
							|  |  |  |  |                  <li key={idx}>{message}</li> | 
					
						
							|  |  |  |  |               ) | 
					
						
							|  |  |  |  |             }) | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         </ul> | 
					
						
							|  |  |  |  |       </div> | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | const Provider = ReactRedux.Provider; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class AppWrapper extends React.Component { | 
					
						
							|  |  |  |  |   // Change code below this line | 
					
						
							|  |  |  |  |   render() { | 
					
						
							|  |  |  |  |     return ( | 
					
						
							|  |  |  |  |       <Provider store = {store}> | 
					
						
							|  |  |  |  |         <DisplayMessages/> | 
					
						
							|  |  |  |  |       </Provider> | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  |   // Change code above this line | 
					
						
							|  |  |  |  | }; | 
					
						
							|  |  |  |  | ``` |