这有点困惑,但我一整天都在努力,所以我有点疲惫。
我正在尝试做一些在 JQuery 中需要 20 秒但在 React 中需要花 20 秒的事情 ́_(ツ)_/́
本质上,在我的应用程序中,标签具有与其相关的重要性 (1-9)。很棒的东西。
我有一个列表组,它按重要性顺序返回我的标签:
const TagScreen = ({
tags
}) =>
{
return(
<ListGroup>
{ tags.sort(function(a,b) {return a.importance < b.importance}).map((tag) =>
( <span key={tag.id}>
<ListGroupItem className='taglist' bsStyle="success"><p>{tag.name}</p>
<ListItem
active={tag.importance} />
</ListGroupItem>
</span>
)
)}
</ListGroup>
)
}
ListItem 是一个(非常)命名不佳的组件(当我完成这个工作时,需要进行大量的重构),但它本质上是一个重要性选择器。它看起来像这样:
const ListItem = ({index, active}) =>
{
var rows = [];
for (var i = 1; i < 10; i++) {
var style = '';
if (active === i) { style = "active" }
rows.push(<li className={style}><a href='#'>{i}</a></li>)
}
return(
<ul className="pagination pull-right pagination-sm">
{ rows.map((row) => row)}
</ul>
)
}
再说一遍,我确信这很糟糕,我是一个 React 新手。本质上,我想要的只是将分页元素上的“事件”样式(从标签的当前重要性开始)更改为单击的样式。我已经尝试了很多很多来做到这一点。一旦我有了这个该死的样式来更改 onClick,我将处理服务器的实际 ping 并稍后更改标记的重要性。
感谢您的帮助。
最佳答案
好吧,您需要确保有一个状态来保存更改,因此您需要一个包含状态的容器组件,或者使用像 redux 这样的东西来处理容器属性的状态
之后你可以这样做
const RangeSelector = ({min, max, current, onSelected}) => {
let items = [];
for (let i = min; i < max; i++) {
items.push(<li className={i === current && 'active'} onClick={(...args) => onSelected(i, ...args)}>{i}</li>);
}
return <ul>{ items }</ul>;
};
const Tag = props => {
return <div>
<h2>{ props.title }<small> ({ props.importance })</small></h2>
<RangeSelector current={ props.importance } min={1} max={11} onSelected={ (...args) => props.onUpdate( props.id, ...args ) } />
</div>;
};
class TagContainer extends React.Component {
constructor(...args) {
super(...args);
this.state = {
tags: [
{ id: 0, title: 'C#', importance: 5 },
{ id: 1, title: 'JavaScript', importance: 1 }
] };
}
updateTag(id, value) {
this.setState( this.state.tags.reduce( (current, item) => {
if (item.id === id) {
item.importance = value;
}
current.push( item );
return current;
}, [] ) );
}
render() {
return <div>
<h1>Tags</h1>
<div className="tag-list">
{ this.state.tags.map( (tag, key) => <Tag onUpdate={(...args) => this.updateTag(...args)} key={tag.id} {...tag} /> ) }
</div>
</div>;
}
}
ReactDOM.render( <TagContainer />, document.querySelector('#app') );
.active { background-color: green; color: white; }
ul { list-style-type: none; display: inline-block; }
ul li { clear: none; display: inline-block; padding: 5px; }
ul li:hover { cursor: pointer; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
所以上面这个小应用程序的细节如下:
- 1 个容器组件,名为
TagContainer
它保存标签的状态,并在子组件发生更改时更新其状态。它以初始状态开始,您可以在构造函数中看到该状态:
constructor(...args) {
super(...args);
this.state = {
tags: [
{ id: 0, title: 'C#', importance: 5 },
{ id: 1, title: 'JavaScript', importance: 1 }
] };
}
这意味着,我将有 2 个标签,一个使用 C#,一个使用 JavaScript,重要性分别为 5 和 1
在渲染容器组件期间,它会渲染一个名为 Tag
的无状态组件,并在其 props 上插入一个更新函数,您可以在此处看到:
<Tag onUpdate={(...args) => this.updateTag(...args)} key={tag.id} {...tag} />
这表明,当调用 onUpdate
时,它将把更新转发给 this.updateTag()
函数,该函数将更新状态。发送的所有参数也将转发到该函数。箭头函数确保 this
的上下文引用当前 TagContainer 实例。
更新状态是通过 this.setState()
函数完成的,您可以在其中发送更新后的状态,以便 React 可以重新渲染容器组件。
现在这可能看起来过于复杂,而且可能确实如此,但它将重新创建状态中的标签数组,并更新要更改的一个状态
this.setState( this.state.tags.reduce( (current, item) => {
if (item.id === id) {
item.importance = value;
}
current.push( item );
return current;
}, [] ) );
id
和 value
通过上面定义的 2 个无状态组件传入。
- 2 个无状态组件,
RangeSelector
和Tag
RangeSelector
只负责显示某个范围内的可用值,并为当前选定的元素设置事件类。当它被点击时,它将通过从 Tag
组件接收到的 onSelected
方法发送值。
Tag
组件负责显示当前标签,并呈现标题和RangeSelector
。它从 TagContainer
组件接收一个 onUpdate
方法,当它被 RangeSelector
调用时,它会添加 1 个额外参数并将其转发到 TagContainer
组件。
最后,状态发生变化,react 将重新渲染您的应用程序,从而导致 DOM 的更新(因为虚拟 dom 和真实 DOM 之间存在差异)
关于javascript - 在 React 中动态更改列表项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46916029/