Tôi có một thành phần rất đơn giản với trường văn bản và nút:
Nó lấy một danh sách làm đầu vào và cho phép người dùng chuyển qua danh sách.
Thành phần có mã sau:
import * as React from "react";
import {Button} from "@material-ui/core";
interface Props {
names: string[]
}
interface State {
currentNameIndex: number
}
export class NameCarousel extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { currentNameIndex: 0}
}
render() {
const name = this.props.names[this.state.currentNameIndex].toUpperCase()
return (
<div>
{name}
<Button onClick={this.nextName.bind(this)}>Next</Button>
</div>
)
}
private nextName(): void {
this.setState( (state, props) => {
return {
currentNameIndex: (state.currentNameIndex + 1) % props.names.length
}
})
}
}
Thành phần này hoạt động rất tốt, ngoại trừ tôi đã không xử lý trường hợp khi trạng thái thay đổi. Khi trạng thái thay đổi, tôi muốn đặt lại currentNameIndex
về không.
Cách tốt nhất để làm việc này là gì?
Các tùy chọn tôi đã xem xét:
Sử dụng componentDidUpdate
Giải pháp này rất khó hiểu, vì componentDidUpdate
chạy sau khi kết xuất, vì vậy tôi cần thêm một mệnh đề trong phương thức kết xuất thành "không làm gì" trong khi thành phần ở trạng thái không hợp lệ, nếu tôi không cẩn thận, tôi có thể gây ra ngoại lệ con trỏ null .
Tôi đã bao gồm một thực hiện này dưới đây.
Sử dụng getDerivedStateFromProps
Các getDerivedStateFromProps
phương pháp là static
và chữ ký chỉ cung cấp cho bạn truy cập vào các đạo cụ nhà nước và tiếp theo hiện hành. Đây là một vấn đề bởi vì bạn không thể biết nếu các đạo cụ đã thay đổi. Kết quả là, điều này buộc bạn phải sao chép các đạo cụ vào trạng thái để bạn có thể kiểm tra xem chúng có giống nhau không.
Làm cho thành phần "được kiểm soát hoàn toàn"
Tôi không muốn làm điều này. Thành phần này nên sở hữu riêng những gì chỉ số hiện được chọn.
Làm cho thành phần "hoàn toàn không được kiểm soát với một khóa"
Tôi đang xem xét phương pháp này, nhưng không thích cách nó khiến phụ huynh cần hiểu chi tiết thực hiện của trẻ.
Linh tinh
Tôi đã dành rất nhiều thời gian để đọc Bạn có lẽ không cần Nhà nước xuất phát nhưng phần lớn không hài lòng với các giải pháp được đề xuất ở đó.
Tôi biết rằng các biến thể của câu hỏi này đã được hỏi nhiều lần, nhưng tôi không cảm thấy như bất kỳ câu trả lời nào cân nhắc các giải pháp có thể. Một số ví dụ về các bản sao:
- Làm thế nào để thiết lập lại trạng thái trong một thành phần thay đổi prop
- Cập nhật trạng thái thành phần khi đạo cụ thay đổi
ruột thừa
Giải pháp sử dụng componetDidUpdate
(xem mô tả ở trên)
import * as React from "react";
import {Button} from "@material-ui/core";
interface Props {
names: string[]
}
interface State {
currentNameIndex: number
}
export class NameCarousel extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { currentNameIndex: 0}
}
render() {
if(this.state.currentNameIndex >= this.props.names.length){
return "Cannot render the component - after compoonentDidUpdate runs, everything will be fixed"
}
const name = this.props.names[this.state.currentNameIndex].toUpperCase()
return (
<div>
{name}
<Button onClick={this.nextName.bind(this)}>Next</Button>
</div>
)
}
private nextName(): void {
this.setState( (state, props) => {
return {
currentNameIndex: (state.currentNameIndex + 1) % props.names.length
}
})
}
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>): void {
if(prevProps.names !== this.props.names){
this.setState({
currentNameIndex: 0
})
}
}
}
Giải pháp sử dụng getDerivedStateFromProps
:
import * as React from "react";
import {Button} from "@material-ui/core";
interface Props {
names: string[]
}
interface State {
currentNameIndex: number
copyOfProps?: Props
}
export class NameCarousel extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { currentNameIndex: 0}
}
render() {
const name = this.props.names[this.state.currentNameIndex].toUpperCase()
return (
<div>
{name}
<Button onClick={this.nextName.bind(this)}>Next</Button>
</div>
)
}
static getDerivedStateFromProps(props: Props, state: State): Partial<State> {
if( state.copyOfProps && props.names !== state.copyOfProps.names){
return {
currentNameIndex: 0,
copyOfProps: props
}
}
return {
copyOfProps: props
}
}
private nextName(): void {
this.setState( (state, props) => {
return {
currentNameIndex: (state.currentNameIndex + 1) % props.names.length
}
})
}
}
next
cho thành phần. Nút hiển thị tên và onClick của nó chỉ gọi chức năng tiếp theo, nằm trong phần cha mẹ, xử lý việc đạp xe
currentIndex
. Khinames
đang được cập nhật, phụ huynh chỉ cần đặt lạicurrentIndex