Asked Feb 15th, 2019 4:14 a.m. 1945 1 2
  • 1945 1 2
+2

[ReactJs- Component] Giải pháp cho Form có rất nhiều thẻ input

Share
  • 1945 1 2

E đang gặp phải trường hợp Form có nhiều trường có thẻ input như trong ví dụ dưới đây :

https://codesandbox.io/s/vjwy1q4p25

(e tạm thời ví dụ vậy , thực tế có thể phát sinh thêm nhiều trường hơn)

Với mỗi thẻ input như vậy e cần gắn sự kiện onChange để set lại giá trị của trường đó trong State khi người dùng nhập vào. Vì có nhiều thẻ input quá nên việc viết sự kiện này bị lặp lại code (10 thẻ input => 10 cái sự kiện onChange giống nhau)

Nếu e tách riêng thẻ input thành một Component là InputBox , rồi gọi lại trong component Form thì chỉ cần viết sự kiện onchange một lần, tuy nhiên bên component cha với mỗi component InputBox em lại phải truyền call back thì mới lấy được giá trị bên input kia. Tóm lại là cũng phải viết thêm ngần ấy cái callback.

Nói chung lợi đằng này lại thiệt đằng kia mà xem chừng cách tách ra còn phức tạp hơn.

Nên e muốn hỏi mọi người xemi có cách nào tối ưu công việc này không ạ.

2 ANSWERS


Answered Feb 24th, 2019 8:23 a.m.
Accepted
+3

đầu tiên e nên cbi trước các component dạng View trong form ví dụ Input, Textarea, Select, RangeSlider, .... tất cả mọi thứ mà trong form sẽ có. thứ 2 là tạo 1 mảng hoặc mảng này fetch từ server về sẽ có dạng như sau ví dụ:

Screenshot 2019-02-24 15.07.05.png bây giờ map mảng data trên kia ra để render ra component và sẽ được check theo type của phần tử trong mảng để render ra các Component View tạo trên và khi thực hiện các hàm lấy giá trị của các Component View ( ví dụ onChange,.... ) thì sẽ lưu vào state ví dụ với object là results và các key là name của phần tử trong mảng ví dụ:

Screenshot 2019-02-24 15.20.26.png

Khi này màn hình sẽ hiển thị các component y chang như mảng kia. và nó sẽ là form động cho phép setting từ admin của web và bên ngoài client sẽ hiển thị như vậy luôn

Share
Avatar Hoang vn @wiliamfeng
Feb 25th, 2019 4:43 a.m.

Cám ơn anh nhiều ạ. Cách này thực sự trong sáng và dễ hiểu ạ 😃)

0
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 25th, 2019 4:56 a.m.

@hpvanlong à anh ơi, anh cho e hỏi thêm câu này nữa với ạ: giả sử ban đầu e khởi tạo state thế này:

state = {
    name : "",
    age: "",
    email: "",
    gender : "",
    ....vv
}

Sau đó em lấy tất cả các giá trị trong state ra và gán vào thuộc tính value cho mỗi trường tương ứng.

Khi người dùng thay đổi một một trường bất kỳ => gọi đến sự kiện onChange () => gọi hàm set lại State để cập nhật giá trị mới mà người dùng nhập vào trường đó. Nhưng như vậy khi state được set lại thì code render() lại toàn bộ cho dù các trường kia người dùng không thực hiện thay đổi gì cả. Đây là hệ quả tất yếu, bắt buộc phải vậy hả anh , hay mình có cách khác để khắc phục nó ạ

0
| Reply
Share
Avatar L @hpvanlong
Feb 25th, 2019 3:52 p.m.

@wiliamfeng nên nhớ các Component Form View kia e phải để làm PureComponent nhé. chưa biết thì đọc thêm về PureComponent, shouldComponentUpdate, shallow compare trong react nhé. Và nên nhớ lúc đầu e nên tạo giá trị mặc định bằng cách biến đổi cái mảng ví dụ data như của a ở trên ra để setState mặc định nhé

Screenshot 2019-02-25 22.49.48.png

khi componentDidMount chạy results sẽ ra default như sau

Screenshot 2019-02-25 22.52.27.png

0
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 25th, 2019 11:45 p.m.

cái này e vẫn chưa thật hiểu. Về lifecycle shouldComponentUpdate thì em biết nó dùng để check xem có nên render() lại một component hay không - tuy nhiên trường hợp sử dụng nó là khi một component cha thay đổi dẫn đến việc render() lại một component con một cách không cần thiết => khi đó ta mới dùng shouldComponentUpdate để kiểm tra thông qua cái này :

shouldComponentUpdate(nextProps, nextState) {
  return this.state.value != nextState.value;
}

tức là ta kiểm tra state hiện tại với state kế tiếp có khác nhau hay không.Nếu khác mới cho render() lại. Còn về PureComponent thì e chưa đọc nhiều nhưng hình như nó là dùng để mình bớt phải sử dụng đến shouldComponentUpdate.

Còn đối với câu hỏi trong trường hợp trên của e thì không có quan hệ cha - con trong component vì ở đây chỉ có duy nhất một component Form thôi. Và khi người dùng thay đổi giá trị ở 1 trường nào đó => bắt buộc phải set lại giá trị của trường đó trong state => vì vậy state chắc chắn bị thay đổi => nên khó tránh khỏi việc render() lại toàn bộ Form lắm => e không biết là có cách nào hạn chế việc đó ko ?

Thêm một ý nữa là trong ví dụ của anh ở trên là anh thực hiện việc setState trong componentDidMount() => làm cho component render() thêm lần nữa => trong khi việc này đâu cần thiết vì mình có thể khởi tạo giá trị cho các trường trong state ngay từ đầu cũng được mà ?

E cũng chỉ đưa ra một vài ý kiến trên sự hiểu biết cá nhân của mình thôi. Có chỗ nào sai xót mong anh giảng giải thêm ạ . Tks anh nhiều !

0
| Reply
Share
Avatar L @hpvanlong
Feb 26th, 2019 2:36 a.m.

@wiliamfeng nếu Form của e chỉ cần lấy kết quả khi bấm Submit thì có lẽ kết qủa đó e k nên lưu vào state vì trong trường hợp này a nghĩ Form có cần phải render lại để làm gì đâu. Ở đây a vẫn cho tất cả là PureComponent vì trong các component ở đây k có cái nào có tình trạng renderProps nên theo a cứ để PureComponent đi. NẾU RESULT Ở ĐÂY LÀ STATE nó vẫn sẽ render tất cả mặc dù mình đang dùng PureComponent do props của nó là onChange đang reference tới 1 hàm _handleInputTextChange là hàm HOF và nó bị thay đổi điều này khiến InputText vẫn sẽ re-render nên trong trường hợp này có thể để InputText là component rồi trong shouldComponentUpdate so sánh nextState.value !== this.state.value thì mới cho render vì value này cũng trùng với value truyền qua hàm onChange. NHƯNG THEO A TRONG TRƯỜNG HỢP FORM KHÔNG THAY ĐỔI THÌ K NÊN DÙNG STATE

Ở đây a ví dụ có 1 InputText tính toán cái gì đó ở đây a ví dụ đơn giản chỉ trả ra value ở hàm onChange. 1 Form nhận vào data và trả ra result ở hàm onSubmit là kết quả cuối cùng của form này.

Còn trường hợp vì sao a lại lấy default mà không khởi tạo luôn vì a đang ví dụ data đó từ server trả về và e đâu biết có những trường nào đâu mà tự nhập sẵn trước được

Đây là demo e bật console lên xem nhé. khi e thay đổi InputText của cái nào thì cái đó render thôi xong rồi submit thì vẫn ra kết quả nhé

https://codesandbox.io/s/n439n6p7p

Thực ra vẫn còn 1 cách nữa mà vẫn dùng state. nhưng comment dài quá nên thôi

+1
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 26th, 2019 7:27 a.m.

e cám ơn anh nhiều ạ. Anh cho e hỏi , câu này : THEO A TRONG TRƯỜNG HỢP FORM KHÔNG THAY ĐỔI THÌ K NÊN DÙNG STATE

ý anh có phải là với Form chỉ có chức năng cho người dùng nhập để gửi thông tin đi ( form 1 chiều ) => thì loại này gọi là FORM KHÔNG THAY ĐỔI . Còn một loại Form mà ta có thể chỉnh sửa thông tin của một user nào đó (thường thấy trong mấy trang quản trị) => thì gọi là FORM CÓ THAY ĐỔI phải không anh ?

Anh có thể cho e một vài suggest về những trường hợp như nào thì dùng state và khi nào thì không nên dùng không ạ .

PS: Cách viết code của anh đọc rõ ràng và dễ hiểu lắm. :: [ Ước gì mấy anh trong Công ty e cũng có phong cách viết như vậy thì tốt quá : ))

0
| Reply
Share
Avatar L @hpvanlong
Feb 26th, 2019 2:04 p.m.

@wiliamfeng đúng rồi e. Form của e nếu có chức năng sửa ( hoặc form của e có cần sử dụng state để làm việc gì đó ). khi ở ngoài bài viết chẳng hạn e bấm vào edit nó mở lại cái form cũ nhưng đồng thời nhập vào những trường cũ để mình edit lại. lúc này đòi hỏi e phải sử dụng state để render lại form đó. Để làm điều này tốt thì những Component View e xài PureComponent để tránh tình trạng render k cần thiết cũng giống như shouldComponentUpdate thôi và props lúc đó e truyền vào nếu k thay đổi thì nó sẽ k bị re-render. A cũng đang bận nên chỉ làm qua 1 chút cho e hiểu vấn đề thôi. E làm dần rồi sẽ hiểu.

E k nên viết hàm kiểu này như bạn ở dưới cmt nhé <input onChange={value => this.handleChange(value)} /> bởi vì khi e change nó luôn create 1 function mới hoàn toàn performance k tốt và sẽ gây ra re-render kể cả khi input kia là 1 PureComponent. e nên viết ví dụ thế này <input onChange={this.handleChange} /> khi đó sẽ là tham chiếu hàm handleChange = value => {}

PS: Trước a cũng xem khá nhiều tut rồi tổng hợp những cách viết hay và đúng đắn nhất thôi. Cách viết code thì nhiều chỗ xử lý theo a thì nên tách ra thành function programming Pure Function dạng function có đầu vào và đầu ra rõ ràng nhưng ở kia thì a cũng viết luôn kiểu bt cho e dễ hiểu, 2 là nên tách rõ Component UI riêng ra, và có thể chia component theo atomic design

+1
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 28th, 2019 3:43 a.m.

@hpvanlong Vâng anh. Cám ơn anh nhiều ạ 😃)

0
| Reply
Share
Answered Feb 15th, 2019 6:37 a.m.
+2

Đây là cách mà mình vẫn thường dùng trong thực tế. Bạn tham khảo thử xem, nếu ai có cách hay hơn mình cũng muốn tham khảo

  • Ở component Form, đặt state cho các input này là một array, mỗi phần tử của nó là một object có 2 trường name, value
  • Ở form viết một hàm callback update state dùng truyền vào từng input, hàm này cần nhận một tham số là name để update state cho đúng giá trị tương ứng với input đó, ngoài ra còn một tham số nữa là giá trị cần update
  • Với mỗi input, đơn giản thì không cần viết component riêng làm gì, trong onChange của input đó, cần truyền thêm name tương ứng khi gọi callback, để update state đúng value tương ứng với input đó mà thôi. Như vậy, chúng ta có thể chỉ cần viết 1 callback nhưng có thể truyền cho nhiều input
Share
Avatar Hoang vn @wiliamfeng
Feb 15th, 2019 6:58 a.m.

a cho e hỏi như cách này a bày thì mình cũng phải gắn 10 cái sự kiện onchange={this.handleChange} vào 10 cái input phải không ạ. Nếu như vậy thì giống như cách e làm ở link e dẫn ở trong câu hỏi à?

0
| Reply
Share
Avatar Ahjhj @ahjhj
Feb 15th, 2019 7:19 a.m.

@wiliamfeng Đương nhiên là mỗi thẻ input phải có onChange mới được vì không có cách nào lưu lại giá trị của nó khác. Tuy nhiên, chúng ta có thể dùng vòng lặp để giảm bớt code, kiểu thế này

this.state = {
    inputs: [{name: 'input1', value: null}, {name: 'input2', value: null}, ...]
}

this.state.inputs.forEach(input => {
    return (
        <input onChange={value => this.handleChange(input.name, value)} />
    )
})
+1
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 15th, 2019 7:31 a.m.

@ahjhj hehe cách này em cũng nghĩ đến đầu tiên nhưng ngặt nỗi không áp dụng vào thực tế của e được vì thiết kế nó không theo quy luật , như ví dụ e đưa ra thì các thẻ input xuất hiện liên tiếp nhưng trong thực tế nó không như vậy , thỉnh thoảng nó lại có trường select hoặc checkbox hay textarea chêm vào giữa cơ. Nên không áp dụng đc vòng lặp ạ

0
| Reply
Share
Avatar Won Phạm @won.baria
Feb 16th, 2019 9:07 a.m.

@wiliamfeng các trường điều có onChange mà bạn.

+1
| Reply
Share
Avatar Hoang vn @wiliamfeng
Feb 17th, 2019 1:00 p.m.

@won.baria nhưng mà nếu tách input thành một component thì chỉ phải khái báo sự kiện onChange một lần thôi b

0
| Reply
Share
Viblo
Let's register a Viblo Account to get more interesting posts.