Cập nhật trạng thái thay đổi đạo cụ trong Biểu mẫu phản ứng


183

Tôi gặp sự cố với biểu mẫu Phản ứng và quản lý trạng thái đúng cách. Tôi có một trường đầu vào thời gian trong một hình thức (trong một phương thức). Giá trị ban đầu được đặt làm biến trạng thái getInitialStatevà được truyền từ thành phần cha. Điều này tự nó hoạt động tốt.

Vấn đề xảy ra khi tôi muốn cập nhật giá trị start_time mặc định thông qua thành phần cha. Bản cập nhật xảy ra trong thành phần cha mẹ thông qua setState start_time: new_time. Tuy nhiên, ở dạng của tôi, giá trị start_time mặc định không bao giờ thay đổi, vì nó chỉ được xác định một lần trong getInitialState.

Tôi đã cố gắng sử dụng componentWillUpdateđể buộc thay đổi trạng thái thông qua setState start_time: next_props.start_time, điều này thực sự có tác dụng, nhưng đã gây ra Uncaught RangeError: Maximum call stack size exceededlỗi cho tôi .

Vì vậy, câu hỏi của tôi là, cách cập nhật trạng thái chính xác trong trường hợp này là gì? Tôi đang nghĩ về điều này sai bằng cách nào đó?

Mã hiện tại:

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time”)

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange

Câu trả lời:


285

thành phầnWillReceiveProps được giải thích kể từ phản ứng 16: sử dụng getDerivingStateFromProps thay thế

Nếu tôi hiểu chính xác, bạn có một thành phần cha mẹ đang truyền start_timexuống ModalBodythành phần đó gán nó cho trạng thái của chính nó? Và bạn muốn cập nhật thời gian đó từ cha mẹ, không phải là một thành phần con.

React có một số lời khuyên về việc xử lý tình huống này. (Lưu ý, đây là một bài viết cũ đã bị xóa khỏi web. Đây là một liên kết đến tài liệu hiện tại về các đạo cụ thành phần ).

Sử dụng đạo cụ để tạo trạng thái getInitialStatethường dẫn đến trùng lặp "nguồn sự thật", tức là nơi có dữ liệu thực. Điều này là do getInitialStatechỉ được gọi khi thành phần được tạo lần đầu tiên.

Bất cứ khi nào có thể, hãy tính toán các giá trị nhanh chóng để đảm bảo rằng chúng không bị đồng bộ hóa sau này và gây ra sự cố bảo trì.

Về cơ bản, bất cứ khi nào bạn gán cha mẹ propscho một đứa trẻ, statephương thức kết xuất không phải lúc nào cũng được gọi là cập nhật prop. Bạn phải gọi nó bằng tay, sử dụng componentWillReceivePropsphương thức.

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

83
Không dùng nữa kể từ React 16
anh chàng

7
@dude Chưa được phản đối, những gì bạn đề cập đến chỉ là một mục đích để tham khảo trong tương lai. Tôi xin trích dẫn[..]going to be deprecated in the future
paddotk

7
@poepje Nó có thể không được phản đối, nhưng nó được xem là không an toàn theo tiêu chuẩn hiện tại và có lẽ nên tránh
bỏ qua

12
Vì vậy, cách mới để làm điều này là gì sau khi thành phầnWillReceiveProps không được dùng nữa?
Boris D. Teoharov

4
@Boris Bây giờ nhóm phản ứng về cơ bản là bảo bạn bị nhồi. Họ cung cấp cho bạn một phương thức mới, được gọi là getDerivingStateFromProps. Cái bắt là đây là một phương thức tĩnh. Có nghĩa là bạn không thể làm bất cứ điều gì không đồng bộ để cập nhật trạng thái (vì bạn phải trả lại trạng thái mới ngay lập tức), bạn cũng không thể truy cập các phương thức hoặc trường lớp. Bạn cũng có thể sử dụng ghi nhớ, nhưng điều đó không phù hợp với mọi trường hợp sử dụng. Một lần nữa, nhóm phản ứng muốn giảm bớt cách làm việc của họ. Đó là một quyết định thiết kế cực kỳ ngu ngốc và bất tài.
ig-dev

76

Rõ ràng mọi thứ đang thay đổi .... getDerivingStateFromProps () hiện là chức năng ưa thích.

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(mã trên bởi danburzo @ github)


7
FYI, bạn cũng cần phải quay lại nullnếu không có gì thay đổi ngay sau khi, nếu bạn nên đi cùngreturn null
Ilgıt Yıldırım

@ IlgıtYıldırım - đã chỉnh sửa mã vì 4 người đã đưa ra nhận xét của bạn - nó có thực sự tạo ra sự khác biệt không?
ErichBSchulz

Có một tài nguyên khá tốt đi sâu vào các tùy chọn khác nhau và lý do tại sao bạn sẽ sử dụng getDerivedStateFromPropshoặc ghi nhớ Reacjs.org/blog/2018/06/07/ trên
bỏ qua

2
getDerivingStateFromProps bị buộc phải tĩnh. Có nghĩa là bạn không thể làm bất cứ điều gì không đồng bộ để cập nhật trạng thái, bạn cũng không thể truy cập các phương thức hoặc trường lớp. Một lần nữa, nhóm phản ứng muốn giảm bớt cách làm việc của họ. Đó là một quyết định thiết kế cực kỳ ngu ngốc và bất tài.
ig-dev

39

componentWillReceiveProps đang bị phản đối vì sử dụng nó "thường dẫn đến lỗi và sự không nhất quán".

Nếu một cái gì đó thay đổi từ bên ngoài, xem xét đặt lại thành phần con hoàn toàn vớikey .

Cung cấp một keychỗ dựa cho thành phần con đảm bảo rằng bất cứ khi nào giá trị keythay đổi từ bên ngoài, thành phần này được kết xuất lại. Ví dụ,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Về hiệu suất của nó:

Trong khi điều này nghe có vẻ chậm, sự khác biệt hiệu suất thường không đáng kể. Sử dụng một khóa thậm chí có thể nhanh hơn nếu các thành phần có logic nặng chạy trên các bản cập nhật vì sự khác biệt bị bỏ qua cho cây con đó.


1
Chìa khóa, bí mật! Hoạt động hoàn hảo trong React 16 như đã đề cập ở trên
Darren Sweeney

khóa sẽ không hoạt động, nếu đó là một đối tượng và bạn không có một chuỗi duy nhất
user3468806

Key không hoạt động cho các đối tượng, tôi đã làm nó. Tất nhiên tôi đã có một chuỗi duy nhất cho khóa mặc dù.
tsujp

@ user3468806 Nếu nó không phải là một đối tượng phức tạp với các tham chiếu bên ngoài, bạn có thể sử dụng JSON.stringify(myObject)để lấy một khóa duy nhất từ ​​đối tượng của mình.
Roy Prins

24

Ngoài ra còn có thành phầnDidUpdate.

Chức năng đăng ký:

componentDidUpdate(prevProps, prevState, snapshot)

Sử dụng điều này như một cơ hội để hoạt động trên DOM khi thành phần đã được cập nhật. Không được gọi vào ban đầu render.

Xem bạn Có lẽ không cần Điều khoản Nhà nước bắt nguồn , trong đó mô tả Chống mẫu cho cả componentDidUpdategetDerivedStateFromProps. Tôi tìm thấy nó rất hữu ích.


Tôi kết thúc việc sử dụng componentDidUpdatevì nó đơn giản và phù hợp hơn với hầu hết các trường hợp.
KeitelDOG

14

Cách móc mới để thực hiện việc này là sử dụng useEffect thay vì thành phầnWillReceiveProps theo cách cũ:

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

trở thành như sau trong một thành phần móc điều khiển chức năng:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

chúng tôi đặt trạng thái bằng setState, sử dụng useEffect, chúng tôi kiểm tra các thay đổi đối với prop được chỉ định và thực hiện hành động để cập nhật trạng thái khi thay đổi prop.


5

Có lẽ bạn không cần nhà nước xuất phát

1. Đặt khóa từ cha mẹ

Khi một khóa thay đổi, React sẽ tạo một cá thể thành phần mới thay vì cập nhật phiên bản hiện tại. Khóa thường được sử dụng cho danh sách động nhưng cũng hữu ích ở đây.

2. Sử dụng getDerivedStateFromProps/componentWillReceiveProps

Nếu khóa không hoạt động vì một số lý do (có lẽ thành phần này rất tốn kém để khởi tạo)

Bằng cách sử dụng, getDerivedStateFromPropsbạn có thể đặt lại bất kỳ phần nào của trạng thái nhưng có vẻ như có một chút lỗi tại thời điểm này (v16.7)!, Xem liên kết ở trên để biết cách sử dụng


2

Từ tài liệu phản ứng: https://reactjs.org/blog/2018/06/07/you-probossible-dont-need-deriving-state.html

Xóa trạng thái khi thay đổi đạo cụ là kiểu chống

Kể từ React 16, thành phầnWillReceiveProps không được dùng nữa. Từ tài liệu phản ứng, cách tiếp cận được đề nghị trong trường hợp này là sử dụng

  1. Kiểm soát đầy đủ thành phần: các ParentComponentcủa ModalBodysẽ sở hữu start_timenhà nước. Đây không phải là cách tiếp cận ưa thích của tôi trong trường hợp này vì tôi nghĩ phương thức nên sở hữu trạng thái này.
  2. Thành phần hoàn toàn không được kiểm soát với một khóa: đây là cách tiếp cận ưa thích của tôi. Một ví dụ từ tài liệu phản ứng: https://codesandbox.io/s/6v1znlxyxn . Bạn sẽ hoàn toàn sở hữu start_timetrạng thái từ của bạn ModalBodyvà sử dụng getInitialStategiống như bạn đã làm. Để thiết lập lại start_timetrạng thái, bạn chỉ cần thay đổi phím từParentComponent


0

Sử dụng Ghi nhớ

Đạo hàm nhà nước của op là một thao tác trực tiếp của đạo cụ, không cần phải có đạo hàm thực sự. Nói cách khác, nếu bạn có một prop có thể được sử dụng hoặc biến đổi trực tiếp thì không cần phải lưu trữ prop trên trạng thái .

Cho rằng giá trị trạng thái start_timechỉ đơn giản là prop start_time.format("HH:mm"), thông tin chứa trong prop đã đủ để cập nhật thành phần.

Tuy nhiên, nếu bạn chỉ muốn gọi định dạng khi thay đổi prop, cách chính xác để thực hiện điều này theo tài liệu mới nhất sẽ là thông qua Ghi nhớ: https://reactjs.org/blog/2018/06/07/you-probossible-dont- Need-origin-state.html # what-about-memoization


-1

Tôi nghĩ rằng sử dụng ref là an toàn cho tôi, không cần quan tâm đến một số phương pháp ở trên.

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}

Tôi nghĩ rằng phản hồi này là khó hiểu (mã khó đọc và không có bất kỳ lời giải thích / liên kết nào với vấn đề của OP) và không giải quyết vấn đề của OP, đó là cách xử lý trạng thái ban đầu.
netchkin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.