redux todomvc:新项目时会发生什么?
redux todomvc: what happens when new one item?
我正在学习redux todomvc,对下面的源代码有一些疑问。欢迎任何意见。谢谢
Q1: 为什么 store.dispatch() 和 store.subscribe() 没有被调用?看来这个例子和这里的data flow introduction有点不一样。
Q2:谁能解释一下当新的一个项目时会发生什么? src/index.js, src/containers/App.js, src/components/Header.js, src/components/TodoTextInput.js 如何在新项目时一起工作?
Q3:todos和actions来自(src/containers/App.js)在哪里?
Q4:这个状态===store.getState()(在src/components/TodoTextInput.js)?
// src/index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import reducer from './reducers'
import 'todomvc-app-css/index.css'
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// src/containers/App.js
import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Header from '../components/Header'
import MainSection from '../components/MainSection'
import * as TodoActions from '../actions'
const App = ({todos, actions}) => (//Q3: where are todos and actions from?
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
todos: state.todos
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
// src/components/Header.js
import React, { PropTypes, Component } from 'react'
import TodoTextInput from './TodoTextInput'
export default class Header extends Component {
static propTypes = {
addTodo: PropTypes.func.isRequired
}
handleSave = text => {
if (text.length !== 0) {
this.props.addTodo(text)
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo// where is it from?
onSave={this.handleSave}
placeholder="What needs to be done?" />
</header>
)
}
}
// src/components/TodoTextInput.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
export default class TodoTextInput extends Component {
static propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
}
state = {//Q4: this state === store.getState()?
text: this.props.text || ''
}
handleSubmit = e => {
const text = e.target.value.trim()
if (e.which === 13) {
this.props.onSave(text)
if (this.props.newTodo) {
this.setState({ text: '' })
}
}
}
handleChange = e => {
this.setState({ text: e.target.value })
}
handleBlur = e => {
if (!this.props.newTodo) {
this.props.onSave(e.target.value)
}
}
render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus="true"
value={this.state.text}
onBlur={this.handleBlur}
onChange={this.handleChange}
onKeyDown={this.handleSubmit} />
)
}
}
// src/components/TodoItem.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import TodoTextInput from './TodoTextInput'
export default class TodoItem extends Component {
static propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
}
state = {
editing: false
}
handleDoubleClick = () => {
this.setState({ editing: true })
}
handleSave = (id, text) => {
if (text.length === 0) {
this.props.deleteTodo(id)
} else {
this.props.editTodo(id, text)
}
this.setState({ editing: false })
}
render() {
const { todo, completeTodo, deleteTodo } = this.props
let element
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo.id, text)} />
)
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo.id)} />
<label onDoubleClick={this.handleDoubleClick}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo.id)} />
</div>
)
}
return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
)
}
}
Q1 why store.dispatch() and store.subscribe() not called?
因为'container'。在 redux 中,容器是一个订阅 store 变化的组件。这是通过 Redux 的 mapStateToProps
,mapDispatchToProps
和最后 container file
中的 connect
函数调用完成的
connect
函数在内部调用 store.subscribe
。
Q2: can anyone explain what happens when new one item?
App
容器通过 mapDispatchToProps
将 actions
属性传递给 App
组件
这个道具 actions
包含动作 addTodo
并且向下传递到 Header
Header
组件在 TextInput
上调用 addTodo
操作是 saved
动作addTodo
被分派
reducer handles 动作并用新项目更新状态。商店已更新。
商店更新触发 App
容器使用更新的道具重新渲染,因为 App
容器有 mapStateToProps
完成
Q3: where are todos and actions from (src/containers/App.js)?
同样,这是因为 Redux 的 connect
功能。它将从 mapStateToProps
和 mapDispatchToProps
获取返回值,合并它们并将其作为 props 传递给 App
组件。 todos
来自 mapStateToProps
而 actions
来自 mapDispatchToProps
Q4 this state === store.getState()
不要混淆。 TodoTextInput
中的 state
是 React 的原生组件状态,与 Redux 的状态无关。但是,如果您的应用程序需要状态,通常需要决定它是应该存在于 Redux 存储中还是应该存在于组件本身中。
如果状态只与组件本身相关,没有其他组件需要知道该状态的状态,则表明它应该存在于组件内部而不是在 Redux 的商店中。
TodoTextInput
组件中的状态在用户提交更改之前暂时保留用户输入。它很适合成为组件本身的内部状态。
我正在学习redux todomvc,对下面的源代码有一些疑问。欢迎任何意见。谢谢
Q1: 为什么 store.dispatch() 和 store.subscribe() 没有被调用?看来这个例子和这里的data flow introduction有点不一样。
Q2:谁能解释一下当新的一个项目时会发生什么? src/index.js, src/containers/App.js, src/components/Header.js, src/components/TodoTextInput.js 如何在新项目时一起工作?
Q3:todos和actions来自(src/containers/App.js)在哪里?
Q4:这个状态===store.getState()(在src/components/TodoTextInput.js)?
// src/index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './containers/App'
import reducer from './reducers'
import 'todomvc-app-css/index.css'
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// src/containers/App.js
import React, { PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Header from '../components/Header'
import MainSection from '../components/MainSection'
import * as TodoActions from '../actions'
const App = ({todos, actions}) => (//Q3: where are todos and actions from?
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
)
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
todos: state.todos
})
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(TodoActions, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
// src/components/Header.js
import React, { PropTypes, Component } from 'react'
import TodoTextInput from './TodoTextInput'
export default class Header extends Component {
static propTypes = {
addTodo: PropTypes.func.isRequired
}
handleSave = text => {
if (text.length !== 0) {
this.props.addTodo(text)
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo// where is it from?
onSave={this.handleSave}
placeholder="What needs to be done?" />
</header>
)
}
}
// src/components/TodoTextInput.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
export default class TodoTextInput extends Component {
static propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
}
state = {//Q4: this state === store.getState()?
text: this.props.text || ''
}
handleSubmit = e => {
const text = e.target.value.trim()
if (e.which === 13) {
this.props.onSave(text)
if (this.props.newTodo) {
this.setState({ text: '' })
}
}
}
handleChange = e => {
this.setState({ text: e.target.value })
}
handleBlur = e => {
if (!this.props.newTodo) {
this.props.onSave(e.target.value)
}
}
render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus="true"
value={this.state.text}
onBlur={this.handleBlur}
onChange={this.handleChange}
onKeyDown={this.handleSubmit} />
)
}
}
// src/components/TodoItem.js
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import TodoTextInput from './TodoTextInput'
export default class TodoItem extends Component {
static propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
}
state = {
editing: false
}
handleDoubleClick = () => {
this.setState({ editing: true })
}
handleSave = (id, text) => {
if (text.length === 0) {
this.props.deleteTodo(id)
} else {
this.props.editTodo(id, text)
}
this.setState({ editing: false })
}
render() {
const { todo, completeTodo, deleteTodo } = this.props
let element
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo.id, text)} />
)
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo.id)} />
<label onDoubleClick={this.handleDoubleClick}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo.id)} />
</div>
)
}
return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
)
}
}
Q1 why store.dispatch() and store.subscribe() not called?
因为'container'。在 redux 中,容器是一个订阅 store 变化的组件。这是通过 Redux 的 mapStateToProps
,mapDispatchToProps
和最后 container file
connect
函数调用完成的
connect
函数在内部调用 store.subscribe
。
Q2: can anyone explain what happens when new one item?
App
容器通过mapDispatchToProps
将 这个道具
actions
包含动作addTodo
并且向下传递到Header
Header
组件在TextInput
上调用addTodo
操作是 saved动作
addTodo
被分派reducer handles 动作并用新项目更新状态。商店已更新。
商店更新触发
App
容器使用更新的道具重新渲染,因为App
容器有mapStateToProps
完成
actions
属性传递给 App
组件
Q3: where are todos and actions from (src/containers/App.js)?
同样,这是因为 Redux 的 connect
功能。它将从 mapStateToProps
和 mapDispatchToProps
获取返回值,合并它们并将其作为 props 传递给 App
组件。 todos
来自 mapStateToProps
而 actions
来自 mapDispatchToProps
Q4 this state === store.getState()
不要混淆。 TodoTextInput
中的 state
是 React 的原生组件状态,与 Redux 的状态无关。但是,如果您的应用程序需要状态,通常需要决定它是应该存在于 Redux 存储中还是应该存在于组件本身中。
如果状态只与组件本身相关,没有其他组件需要知道该状态的状态,则表明它应该存在于组件内部而不是在 Redux 的商店中。
TodoTextInput
组件中的状态在用户提交更改之前暂时保留用户输入。它很适合成为组件本身的内部状态。