* chore(i8n,learn): processed translations * Update curriculum/challenges/chinese/01-responsive-web-design/applied-visual-design/use-the-u-tag-to-underline-text.md Co-authored-by: Randell Dawson <5313213+RandellDawson@users.noreply.github.com> Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> Co-authored-by: Randell Dawson <5313213+RandellDawson@users.noreply.github.com>
		
			
				
	
	
	
		
			6.0 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	id, title, challengeType, forumTopicId, dashedName
| id | title | challengeType | forumTopicId | dashedName | 
|---|---|---|---|---|
| 5a24c314108439a4d4036144 | 使用 Provider 连接 Redux 和 React | 6 | 301435 | use-provider-to-connect-redux-to-react | 
--description--
在上一挑战中,创建了 Redux store 和 action,分别用于处理消息数组和添加新消息。 下一步要为 React 提供访问 Redux store 及发起更新所需的 actions。 react-redux 包可帮助我们完成这些任务。
React Redux 提供的 API 有两个关键的功能:Provider 和 connect。 会在另一个挑战会介绍 connect。 Provider是 React Redux 包装 React 应用的 wrapper 组件, 它允许访问整个组件树中的 Redux store 及 dispatch(分发)方法。 Provider 需要两个 props:Redux store 和 App 应用的子组件。 用于 App 组件的 Provider 可这样定义:
<Provider store={store}>
  <App/>
</Provider>
--instructions--
此时,编辑器上显示的是过去几个挑战中所有代码, 包括 Redux store、actions、DisplayMessages 组件。 新出现的代码是底部的AppWrapper组件, 这个顶级组件可用于渲染 ReactRedux 的 Provider,并把 Redux 的 store 作为 props 传入。 接着,渲染 DisplayMessages 为子组件。 完成这些任务后,会看到 React 组件渲染到页面上。
注意: React Redux 在此可作全局变量,因此可通过点号表示法访问 Provider。 利用这一点,编辑器上的代码把 Provider 设置为常量,便于你在 AppWrapper 渲染方法中使用。
--hints--
AppWrapper 应渲染。
assert(
  (function () {
    const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
    return mockedComponent.find('AppWrapper').length === 1;
  })()
);
Provider 组件应传入相当于 Redux store 的 store 参数。
(getUserInput) =>
  assert(
    (function () {
      const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
      return __helpers
        .removeWhiteSpace(getUserInput('index'))
        .includes('<Providerstore={store}>');
    })()
  );
DisplayMessages 应渲染为 AppWrapper 的子组件。
assert(
  (function () {
    const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
    return (
      mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1
    );
  })()
);
DisplayMessages 组件应渲染 h2、input、button、ul 四个元素。
assert(
  (function () {
    const mockedComponent = Enzyme.mount(React.createElement(AppWrapper));
    return (
      mockedComponent.find('div').length === 1 &&
      mockedComponent.find('h2').length === 1 &&
      mockedComponent.find('button').length === 1 &&
      mockedComponent.find('ul').length === 1
    );
  })()
);
--seed--
--after-user-code--
ReactDOM.render(<AppWrapper />, document.getElementById('root'))
--seed-contents--
// 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
};
--solutions--
// 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
};