Phản ứng: tại sao thành phần con không cập nhật khi thay đổi prop


135

Tại sao trong ví dụ mã giả sau đây Trẻ không kết xuất lại khi Container thay đổi foo.bar?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

Ngay cả khi tôi gọi forceUpdate()sau khi sửa đổi giá trị trong Container, Child vẫn hiển thị giá trị cũ.


5
Đây có phải là mã của bạn? Có vẻ như đó không phải là mã React hợp lệ

Tôi nghĩ rằng giá trị đạo cụ không nên thay đổi trong thành phần container thay vào đó nên thay đổi thành phần cha mẹ bằng setState và trạng thái đó phải được ánh xạ tới các đạo cụ chứa
Piyush Patel

Sử dụng toán tử trải rộng như thế này <Thanh con = {... this.props.foo.bar} />
Sourav Singh

@AdrianWydmanski và 5 người khác đã nâng cấp: en.wikipedia.org/wiki/Pseudocode
David Newcomb

Các đạo cụ @PiyushPatel được cập nhật khi một thành phần được kết xuất lại tại chỗ như ví dụ về chương trình mã giả. Một ví dụ khác về điều này là với một cái gì đó như sử dụng <Route exact path="/user/:email" component={ListUserMessagePage} />, một liên kết trên cùng một trang sẽ cập nhật các đạo cụ mà không cần tạo một thể hiện mới và chạy các sự kiện vòng đời thông thường.
David Newcomb

Câu trả lời:


94

Cập nhật con để có 'khóa' thuộc tính bằng tên. Thành phần sẽ kết xuất lại mỗi khi phím thay đổi.

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}

5
Cảm ơn vì điều đó. Tôi đã sử dụng cửa hàng redux và không thể khiến thành phần của mình hiển thị lại cho đến khi tôi thêm khóa. (Tôi đã sử dụng thư viện uuid để tạo khóa ngẫu nhiên và nó hoạt động).
Maiya

Sử dụng hook con tôi sẽ không kết xuất lại khi thiết lập trạng thái trên một mảng hiện có. Bản hack (có lẽ là ý tưởng tồi) của tôi là đồng thời thiết lập trạng thái của một chỗ dựa giả và thêm nó vào mảng phụ thuộc useEffect : [props.notRenderingArr, props.dummy]. Nếu độ dài của mảng luôn thay đổi, bạn có thể đặt mảng phụ thuộc useEffect thành [props.notRenderingArr.length].
hipnosis

5
đây cũng là điều tôi cần Tôi hoang mang, khi nào là keybắt buộc, so với chỉ setState? Tôi có một số trẻ em dựa vào trạng thái của cha mẹ nhưng chúng không kích hoạt kết xuất lại ...
dcsan

Câu trả lời chính xác. Nhưng tại sao lại có hành vi này?
Rishav

1
Tôi đã sử dụng chỉ mục làm khóa nhưng nó không hoạt động đúng. Bây giờ tôi đang sử dụng uuid và nó hoàn hảo :) Cảm ơn @Maiya
Ali Rehman

90

Bởi vì trẻ em không chạy lại nếu đạo cụ của cha mẹ thay đổi, nhưng nếu NHÀ NƯỚC của nó thay đổi :)

Những gì bạn đang hiển thị là đây: https://facebook.github.io/react/tips/c truyềnicate-b between-components.html

Nó sẽ truyền dữ liệu từ cha mẹ sang con thông qua các đạo cụ nhưng không có logic tái lập ở đó.

Bạn cần thiết lập một số trạng thái cho cha mẹ sau đó đăng ký lại cho con về trạng thái thay đổi của cha mẹ. Điều này có thể giúp đỡ. https://facebook.github.io/react/tips/expose-component-fifts.html


5
Tại sao setState sẽ gây ra kết xuất lại nhưng forceUpdate thì không? Cũng hơi lạc đề, nhưng các thành phần được cập nhật như thế nào khi các đạo cụ được truyền từ redux và trạng thái của nó được cập nhật thông qua hành động?
Tuomas Toivonen

đạo cụ không được cập nhật ngay cả khi bạn cập nhật thành phần đạo cụ bạn đã vượt qua vẫn còn đó. Flux là một chủ đề khác có :)
François Richard

3
bang! = đạo cụ bạn cần đọc thêm về điều đó. Bạn không thực hiện cập nhật với đạo cụ
François Richard

bạn có thể thấy nó trực tiếp trong mã nguồn, đơn giản là nó không phải là quá trình tương tự
Webdess

Vì vậy, những gì bạn nói ở đây đã bị thay đổi hoặc tôi không hiểu chính xác, tôi đã đặt câu hỏi cho điều đó, stackoverflow.com/questions/58903921/
Lỗi

50

Tôi đã từng gặp vấn đề tương tự. Đây là giải pháp của tôi, tôi không chắc đó là cách thực hành tốt, hãy nói cho tôi biết nếu không:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

CẬP NHẬT: Bây giờ bạn có thể làm điều tương tự bằng React Hook: (chỉ khi thành phần là một hàm)

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);

3
Đây chắc chắn là câu trả lời chính xác, không đề cập đến đơn giản nhất
Guilherme Ferreira

1
Tôi cũng đang sử dụng phương pháp này.
thô

@ vancy-quần Trong componentDidUpdatetrường hợp này đi trong thành phần Trẻ em.
Nick Friskel

4
Bạn không cần và không nên chuyển đổi đạo cụ sang trạng thái trong một thành phần con. Chỉ cần sử dụng đạo cụ trực tiếp. Trong FunctionComponentsđó sẽ tự động cập nhật các thành phần con nếu đạo cụ thay đổi.
OEMera

1
Cách tiếp cận này cũng hoạt động khi bạn có đạo cụ trong các thành phần con có nguồn gốc từ trạng thái cha mẹ
Chefk5

10

Theo thành phần triết lý React không thể thay đổi đạo cụ của nó. chúng nên được nhận từ cha mẹ và nên bất biến. Chỉ có cha mẹ có thể thay đổi đạo cụ của con cái của nó.

giải thích tốt về nhà nước vs đạo cụ

Ngoài ra, hãy đọc chủ đề này Tại sao tôi không thể cập nhật đạo cụ trong Reac.js?


Điều này cũng không có nghĩa là container không thể sửa đổi các đạo cụ mà nó nhận được từ cửa hàng redux?
Tuomas Toivonen

3
Container không nhận được đạo cụ trực tiếp từ cửa hàng, chúng được cung cấp bằng cách đọc một phần của cây trạng thái redux (khó hiểu ??). !! nhầm lẫn là bởi vì chúng ta không biết về chức năng ma thuật kết nối bằng phản ứng-redux. nếu bạn thấy mã nguồn của phương thức kết nối, về cơ bản, nó tạo ra một thành phần bậc cao hơn có trạng thái với property storeState và được đăng ký vào cửa hàng redux. khi storeState thay đổi toàn bộ kết xuất lại xảy ra và các đạo cụ đã sửa đổi được cung cấp cho vùng chứa bằng cách đọc trạng thái đã thay đổi. hy vọng điều này trả lời câu hỏi
Sujan Thakare

Giải thích tốt, suy nghĩ về thành phần bậc cao giúp tôi hiểu những gì đang xảy ra
Tuomas Toivonen

7

Bạn nên sử dụng setStatechức năng. Nếu không, nhà nước sẽ không lưu thay đổi của bạn, bất kể bạn sử dụng ForceUpdate như thế nào.

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

Mã của bạn có vẻ không hợp lệ. Tôi không thể kiểm tra mã này.


Có nên không <Child bar={this.state.foo.bar} />?
Josh Broadhurst

Đúng. Tôi cập nhật câu trả lời. Nhưng như tôi đã nói điều này có thể không phải là một mã hợp lệ. Chỉ cần sao chép từ câu hỏi.
JamesYin

5

Khi tạo các thành phần React từ functionsuseState.

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

Cái này không hoạt động

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

Điều này không hoạt động (thêm khóa)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />

3

Xác nhận, thêm một công trình chính. Tôi đã đi qua các tài liệu để thử và hiểu lý do tại sao.

React muốn hiệu quả khi tạo các thành phần con. Nó sẽ không hiển thị một thành phần mới nếu nó giống với một đứa trẻ khác, khiến trang tải nhanh hơn.

Thêm một khóa buộc React để hiển thị một thành phần mới, do đó đặt lại Trạng thái cho thành phần mới đó.

https://reactjs.org/docs/reconcestion.html#recursing-on-children


2

Sử dụng hàm setState. Vì vậy, bạn có thể làm

       this.setState({this.state.foo.bar:123}) 

bên trong phương thức xử lý sự kiện.

Khi trạng thái được cập nhật, nó sẽ kích hoạt các thay đổi và kết xuất lại sẽ diễn ra.


1
Nó phải là this.setState ({foo.bar:123}) phải không?
Charith Jayasanka

0

Bạn có lẽ nên biến Child thành thành phần chức năng nếu nó không duy trì bất kỳ trạng thái nào và chỉ cần kết xuất các đạo cụ và sau đó gọi nó từ cha mẹ. Thay thế cho điều này là bạn có thể sử dụng các hook với thành phần chức năng (useState) sẽ khiến thành phần không trạng thái hiển thị lại.

Ngoài ra, bạn không nên thay đổi propas vì chúng là bất biến. Duy trì trạng thái của thành phần.

Child = ({bar}) => (bar);

0

Tôi đã gặp phải vấn đề tương tự. Tôi có một Tooltipthành phần đang nhận showTooltipprop, mà tôi đang cập nhật trên Parentthành phần dựa trên một ifđiều kiện, nó đang được cập nhật trong Parentthành phần nhưng Tooltipthành phần không được kết xuất.

const Parent = () => {
   let showTooltip = false;
   if(....){ showTooltip = true; }
   return(
      <Tooltip showTooltip={showTooltip}></Tooltip>
   )
}

Sai lầm tôi đã làm là tuyên bố showTooltiplà cho phép.

Tôi nhận ra những gì tôi đã làm sai Tôi đã vi phạm các nguyên tắc về cách hoạt động của kết xuất, Thay thế nó bằng các móc đã thực hiện công việc.

const [showTooltip, setShowTooltip] =  React.useState<boolean>(false);

0

định nghĩa các đạo cụ đã thay đổi trong mapStateToProps của phương thức kết nối trong thành phần con.

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

Trong trường hợp của tôi, kênh của channelList được cập nhật nên tôi đã thêm chanelList trong mapStateToProps

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.