class Counters extends Component {
state = {
counters: [
{ id: 1,value: 4 },
{ id: 2,value: 0 },
{ id: 3,value: 0 },
{ id: 4,value: 0 }
]
}
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
})
this.setState({ counters });
}
handleDelete = (counterId) => {
const counters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters });
}
render() {
return (<div>
<button
onClick={this.handleReset}
className="btn btn-primary btn-sm m-2">Reset</button>
{ this.state.counters.map(counter =>
<Counter key={counter.id} onDelete={this.handleDelete} counter={counter}/>
)}
</div>);
}
}
class Counter extends Component {
state = {
value: this.props.counter.value,
};
/* constructor(){
super();
this.handleIncrement = this.handleIncrement.bind(this);
} */
handleIncrement = (product) => {
console.log(product);
this.setState({ value: this.state.value + 1});
}
doHandleIncrement = () => {
this.handleIncrement({ id: 1 });
}
render() {
return (
<div>
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button onClick={this.doHandleIncrement} className='btn btn-secondary btn-sm'>Increment</button>
<button onClick={() => this.props.onDelete(this.props.counter.id)} className="btn btn-danger btn-sm m-2">Delete</button>
</div>
);
}
getBadgeClasses() {
let classes = 'badge m-2 badge-';
classes += (this.state.value === 0) ? 'warning' : 'primary';
return classes;
}
formatCount() {
const { value } = this.state;
return value === 0 ? 'Zero' : value;
}
}
我们在顶部添加了一个重置按钮,我们试图通过 handleReset 来重置数据,但是回到浏览器中,我们会发现重置功能并没有实现。这是因为我们没有A single source of Truth(真相的唯一来源)。这里我们面对的,是数据唯一性的问题。每个组件,都有自己的state。
在counter组件中,我们通过 ‘ value: this.props.counter.value ’ 实例化可 state 对象的 value 值,基于它得到的 props 。这行代码只在Counter组件实例创建的时候执行一次,所以当页面加载的时候,我们看到 value 被实例化并可以修改,因为它是本地 state,但是当我们按下重置按钮时,每个 Counter 组件的本地 state 没有更新。
那么怎么解决这个问题,我们需要删掉在Counter组件中的本地State,然后建立一个唯一的数据源。
在Counter组件中,我们想删掉Counter组件的State,只保留props来接收组件所需的数据。我们叫这种类型的组件为受控组件。
受控组件没有自己的state,它所有的数据都来自props,之后再数据需要改变时发起事件。这种控件是完全被其父控件控制的。可解决数据不统一的问题。
删除 Counter 组件中的 state:
class Counter extends Component {
render() {
return (
<div>
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button onClick={() => this.props.onIncrement(this.props.counter)} className='btn btn-secondary btn-sm'>Increment</button>
<button onClick={() => this.props.onDelete(this.props.counter.id)} className="btn btn-danger btn-sm m-2">Delete</button>
</div>
);
}
getBadgeClasses() {
let classes = 'badge m-2 badge-';
classes += (this.props.counter.value === 0) ? 'warning' : 'primary';
return classes;
}
formatCount() {
const { value } = this.props.counter;
return value === 0 ? 'Zero' : value;
}
}
我们完全使用 props对象来呈现数据以及处理数据的修改。我们在 Counters 组件中也需要进行相应的改动:
class Counters extends Component {
state = {
counters: [
{ id: 1,value: 4 },
{ id: 2,value: 0 },
{ id: 3,value: 0 },
{ id: 4,value: 0 }
]
}
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
})
this.setState({ counters });
}
handleIncrement = counter => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = {...counter};
counters[index].value++;
this.setState({ counters })
}
handleDelete = (counterId) => {
const counters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters });
}
render() {
return (<div>
<button
onClick={this.handleReset}
className="btn btn-primary btn-sm m-2">Reset</button>
{ this.state.counters.map(counter =>
<Counter
key={counter.id}
onDelete={this.handleDelete}
onIncrement={this.handleIncrement}
counter={counter}/>
)}
</div>);
}
}
回到浏览器中,我们发现我们已经实现了增加和重置的功能!