Tworzę aplikację, w której użytkownik może zaprojektować własną formę. Np. Podaj nazwę pola i szczegóły, które inne kolumny powinny zostać uwzględnione.
Komponent jest dostępny tutaj jako JSFiddle .
Mój stan początkowy wygląda następująco:
var DynamicForm = React.createClass({
getInitialState: function() {
var items = {};
items[1] = { name: 'field 1', populate_at: 'web_start',
same_as: 'customer_name',
autocomplete_from: 'customer_name', title: '' };
items[2] = { name: 'field 2', populate_at: 'web_end',
same_as: 'user_name',
autocomplete_from: 'user_name', title: '' };
return { items };
},
render: function() {
var _this = this;
return (
<div>
{ Object.keys(this.state.items).map(function (key) {
var item = _this.state.items[key];
return (
<div>
<PopulateAtCheckboxes this={this}
checked={item.populate_at} id={key}
populate_at={data.populate_at} />
</div>
);
}, this)}
<button onClick={this.newFieldEntry}>Create a new field</button>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</div>
);
}
Chcę zaktualizować stan, gdy użytkownik zmieni dowolną z wartości, ale trudno mi celować w odpowiedni obiekt:
var PopulateAtCheckboxes = React.createClass({
handleChange: function (e) {
item = this.state.items[1];
item.name = 'newName';
items[1] = item;
this.setState({items: items});
},
render: function() {
var populateAtCheckbox = this.props.populate_at.map(function(value) {
return (
<label for={value}>
<input type="radio" name={'populate_at'+this.props.id} value={value}
onChange={this.handleChange} checked={this.props.checked == value}
ref="populate-at"/>
{value}
</label>
);
}, this);
return (
<div className="populate-at-checkboxes">
{populateAtCheckbox}
</div>
);
}
});
Jak stworzyć this.setState
, aby zaktualizować items[1].name
?
javascript
reactjs
state
martins
źródło
źródło
Odpowiedzi:
Możesz użyć
update
pomocnika niezmienności do tego :Lub jeśli nie zależy ci na wykrywaniu zmian w tym elemencie
shouldComponentUpdate()
przy użyciu metody cyklu życia===
, możesz bezpośrednio edytować stan i zmusić komponent do ponownego renderowania - jest to faktycznie to samo, co odpowiedź @limelights, ponieważ jest to wyciąganie obiektu ze stanu i edytowanie go.Dodanie po edycji:
Zapoznaj się z lekcją na temat komunikacji z prostym komponentem ze szkolenia reagowania, aby dowiedzieć się, jak przekazać funkcję wywołania zwrotnego z rodzica zarządzającego stanem do komponentu potomnego, który musi wywołać zmianę stanu.
źródło
Ponieważ w tym wątku jest dużo dezinformacji, oto jak możesz to zrobić bez bibliotek pomocniczych:
Możesz połączyć kroki 2 i 3, jeśli chcesz:
Lub możesz zrobić wszystko w jednym wierszu:
Uwaga: Zrobiłem
items
tablicę. OP użył obiektu. Jednak koncepcje są takie same.Możesz zobaczyć, co się dzieje w twoim terminalu / konsoli:
źródło
items[0] === clone[0]
bit w moim przykładzie terminala na dole. Potrójne=
sprawdzenie, czy obiekty odnoszą się do tej samej rzeczy.items.findIndex()
powinien to zrobić krótko.Zła droga!
Jak zauważyło wielu lepszych programistów w komentarzach: mutowanie stanu jest złe!
Zajęło mi to trochę czasu, aby to rozgryźć. Powyżej działa, ale odbiera moc React. Na przykład
componentDidUpdate
nie zobaczy tego jako aktualizacji, ponieważ jest modyfikowany bezpośrednio.Tak więc właściwą drogą byłoby:
źródło
items[1].role = e.target.value
stanu mutacji bezpośrednio?Aby zmodyfikować głęboko zagnieżdżonych / zmiennych w React Państwowej, używane są zwykle trzy sposoby: wanilia JavaScript użytkownika
Object.assign
, niezmienność pomocniczych icloneDeep
od Lodash .Istnieje również wiele innych mniej popularnych bibliotek stron trzecich, aby to osiągnąć, ale w tej odpowiedzi omówię tylko te trzy opcje. Istnieją również dodatkowe waniliowe metody JavaScript, takie jak rozkładanie tablic (patrz na przykład odpowiedź @ mpen), ale nie są one bardzo intuicyjne, łatwe w użyciu i zdolne do obsługi wszystkich sytuacji manipulacji stanem.
Jak wskazano niezliczoną ilość razy w najczęściej głosowanych komentarzach do odpowiedzi, których autorzy proponują bezpośrednią mutację stanu: po prostu nie rób tego . Jest to wszechobecny anty-wzór React, który nieuchronnie doprowadzi do niepożądanych konsekwencji. Naucz się właściwej drogi.
Porównajmy trzy powszechnie stosowane metody.
Biorąc pod uwagę ten stan struktury obiektu:
Możesz użyć następujących metod, aby zaktualizować najbardziej wewnętrznie
inner
pola bez wpływu na resztę stanu.1. Object.assign Vanilla JavaScript
Należy pamiętać, że Object.assign nie wykona głębokiego klonowania , ponieważ kopiuje tylko wartości właściwości , i dlatego to, co robi, nazywa się płytkim kopiowaniem (patrz komentarze).
Aby to zadziałało, powinniśmy jedynie manipulować właściwościami typów pierwotnych (
outer.inner
), czyli ciągów, liczb, boolanów.W tym przykładzie tworzymy nową stałą (
const newOuter...
), używającObject.assign
, która tworzy pusty obiekt ({}
), kopiuje do niegoouter
obiekt ({ inner: 'initial value' }
), a następnie kopiuje{ inner: 'updated value' }
nad nim inny obiekt .W ten sposób na koniec nowo utworzona
newOuter
stała będzie miała wartość od{ inner: 'updated value' }
momentuinner
przesłonięcia właściwości. JestnewOuter
to zupełnie nowy obiekt, który nie jest powiązany z obiektem w stanie, więc można go w razie potrzeby mutować, a stan pozostanie taki sam i nie zmieni się do momentu uruchomienia polecenia aktualizacji.Ostatnią częścią jest użycie
setOuter()
setera do zastąpienia oryginałuouter
w stanie nowo utworzonymnewOuter
obiektem (zmieni się tylko wartość, nazwa właściwościouter
się nie zmieni).Teraz wyobraź sobie, że mamy głębszy stan
state = { outer: { inner: { innerMost: 'initial value' } } }
. Możemy spróbować utworzyćnewOuter
obiekt i zapełnić goouter
treścią ze stanu, aleObject.assign
nie będziemy mogli skopiowaćinnerMost
wartości do tego nowo utworzonegonewOuter
obiektu, ponieważinnerMost
jest on zbyt głęboko zagnieżdżony.Możesz nadal kopiować
inner
, jak w powyższym przykładzie, ale ponieważ jest to teraz obiekt, a nie prymityw, odwołanie znewOuter.inner
zostanie skopiowane doouter.inner
niego, co oznacza, że otrzymamynewOuter
obiekt lokalny bezpośrednio powiązany z obiektem w stanie .Oznacza to, że w tym przypadku mutacje lokalnie utworzonego
newOuter.inner
będą miały bezpośredni wpływ naouter.inner
obiekt (w stanie), ponieważ w rzeczywistości stały się tym samym (w pamięci komputera).Object.assign
dlatego będzie działać tylko wtedy, gdy masz stosunkowo prostą jednopoziomową strukturę stanu głębokiego z najbardziej wewnętrznymi elementami przechowującymi wartości typu pierwotnego.Jeśli masz głębsze obiekty (2. poziom lub więcej), które powinieneś zaktualizować, nie używaj
Object.assign
. Ryzykujesz mutacją stanu bezpośrednio.2. Klon Lodasha
Klon Lodasha jest znacznie prostszy w użyciu. Wykonuje głębokie klonowanie , więc jest solidną opcją, jeśli masz dość złożony stan z wielopoziomowymi obiektami lub tablicami. Tylko
cloneDeep()
właściwość stanu najwyższego poziomu, mutuj sklonowaną część w dowolny sposób isetOuter()
wróć do stanu.3. pomocnik niezmienności
immutability-helper
bierze go na zupełnie nowy poziom, a chłodny rzeczą jest to, że może on nie tylko$set
wartości do elementów państwowych, ale również$push
,$splice
,$merge
(itd.) im. Tutaj jest lista dostępnych poleceń .Notatki dodatkowe
Ponownie pamiętaj, że
setOuter
modyfikuje on tylko właściwości pierwszego poziomu obiektu stanu (outer
w tych przykładach), a nie głęboko zagnieżdżone (outer.inner
). Gdyby zachowywał się inaczej, to pytanie by nie istniało.Który jest odpowiedni dla twojego projektu?
Jeśli nie chcesz lub nie możesz korzystać z zewnętrznych zależności i masz prostą strukturę stanu , trzymaj się
Object.assign
.Jeśli manipulujesz ogromnym i / lub złożonym stanem , Lodash's
cloneDeep
to mądry wybór.Jeśli potrzebujesz zaawansowanych możliwości , tj. Jeśli twoja struktura stanu jest złożona i musisz wykonywać na niej wszelkiego rodzaju operacje, spróbuj
immutability-helper
, jest to bardzo zaawansowane narzędzie, które można wykorzystać do manipulowania stanem.... czy naprawdę naprawdę musisz to zrobić?
Jeśli przechowujesz złożone dane w stanie React, być może jest to dobry moment, aby pomyśleć o innych sposobach ich obsługi. Ustawienie złożonych obiektów stanu bezpośrednio w komponentach React nie jest prostą operacją i zdecydowanie sugeruję zastanowienie się nad różnymi podejściami.
Najprawdopodobniej lepiej nie trzymaj swoich złożonych danych w sklepie Redux, ustawiając je tam za pomocą reduktorów i / lub sag i uzyskując dostęp do nich za pomocą selektorów.
źródło
Object.assign
nie wykonuje głębokiej kopii. Powiedz, jeślia = {c: {d: 1}}
ib = Object.assign({}, a)
wtedy wykonaszb.c.d = 4
, toa.c.d
jest zmutowany.1
najbardziej wewnętrznego obiektu (a.c.d
) zostanie zmutowana. Ale jeśli ponownie przypiszesz następcę pierwszego poziomub
, w ten sposób:b.c = {f: 1}
odpowiednia częśća
nie zostanie zmutowana (pozostanie{d: 1}
). W każdym razie fajny haczyk, od razu zaktualizuję odpowiedź.shallow copy
a niedeep copy
. Łatwo jest pomylić coshallow copy
oznacza. Wshallow copy
,a !== b
ale dla każdego klucza z obiektu źródłowegoa
,a[key] === b[key]
Object.assign
w odpowiedzi.JSON.parse(JSON.stringify(object))
jest również wariantem głębokiego klonowania. Wydajność jest gorsza niż lodashcloneDeep
. Measurethat.net/Benchmarks/Show/2751/0/…Miałem ten sam problem. Oto proste rozwiązanie, które działa!
źródło
{
nawiasów klamrowych zamiast nawiasów, które naprawiłemZgodnie z dokumentacją React na temat setState , używanie
Object.assign
jak sugerują inne odpowiedzi tutaj nie jest idealne. Ze względu na charaktersetState
asynchronicznego zachowania kolejne połączenia przy użyciu tej techniki mogą zastąpić poprzednie połączenia, powodując niepożądane skutki.Zamiast tego doktorzy React zalecają użycie formy aktualizacji,
setState
która działa w poprzednim stanie. Pamiętaj, że przy aktualizacji tablicy lub obiektu musisz zwrócić nową tablicę lub obiekt, ponieważ React wymaga od nas zachowania niezmienności stanu. Używanie operatora rozprzestrzeniania się składni ES6 do płytkiego kopiowania tablicy, tworzenie lub aktualizowanie właściwości obiektu przy danym indeksie tablicy wyglądałoby następująco:źródło
Najpierw weź pożądany element, zmień co chcesz na tym obiekcie i ustaw go z powrotem na stan. Sposób, w jaki używasz stanu tylko przez przekazywanie obiektu,
getInitialState
byłby znacznie łatwiejszy, gdybyś używał obiektu z kluczem.źródło
Uncaught TypeError: Cannot read property 'items' of null
.getInitialState
.items
nie jest nigdzie zdefiniowany.item
odwołanie do własnejthis.state.items[1]
zmiennej stanu. Następnie modyfikujeszitem
(item.name = 'newName'
), a tym samym mutujesz stan bezpośrednio, co jest wysoce odradzane. W twoim przykładzie nie ma nawet potrzeby dzwonieniathis.setState({items: items})
, ponieważ stan jest już zmutowany bezpośrednio.Nie mutuj stanu na miejscu. Może to powodować nieoczekiwane wyniki. Nauczyłem się mojej lekcji! Zawsze pracuj z kopią / klonem,
Object.assign()
jest dobry:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
źródło
items
w twoim przykładzie? Miałeś na myślithis.state.items
czy coś innego?items[1] = item;
powinna znajdować się linijkaitems = this.state.items;
. Uważaj, mój javascript jest zardzewiały i uczę się reagować na mój domowy projekt, więc nie mam pojęcia, czy to dobrze, czy źle :-)To jest naprawdę proste.
Najpierw wyciągnij cały obiekt pozycji ze stanu, zaktualizuj część obiektu pozycji zgodnie z potrzebami i ustaw cały obiekt pozycji z powrotem w stanie za pośrednictwem setState.
źródło
Ponieważ żadna z powyższych opcji nie była dla mnie idealna, użyłem mapy:
źródło
Bez mutacji:
źródło
newItems
jest ustawiony.fo of
lubforEach
mapa jest najszybsza.Okazało się, że jest to zaskakująco trudne i żadna z magii ES6 nie działała zgodnie z oczekiwaniami. Używał takiej struktury, aby uzyskać renderowane właściwości elementu na potrzeby układu.
stwierdzono, że
update
metoda zimmutability-helper
jest najprostsza w tym uproszczonym przykładzie:zgodnie z adaptacją https://github.com/kolodny/immutability-helper#computed-property-names
elementu tablicy, który ma zostać zaktualizowany, jest bardziej zagnieżdżony obiekt złożony, użyj odpowiedniej metody głębokiej kopii oparciu o złożoność
Z pewnością istnieją lepsze sposoby obsługi parametrów układu, ale chodzi o to, jak obsługiwać tablice. Odpowiednie wartości dla każdego elementu potomnego można również obliczyć poza nimi, ale uważam, że wygodniej jest przekazać parametr ContainState w dół, aby dzieci mogły pobierać właściwości do woli i aktualizować macierz stanów nadrzędnych przy danym indeksie.
źródło
Użyj mapy tablic z funkcją strzałki w jednym wierszu
źródło
Użyj zdarzenia,
handleChange
aby dowiedzieć się, który element się zmienił, a następnie zaktualizuj go. W tym celu konieczna może być zmiana niektórych właściwości w celu ich identyfikacji i aktualizacji.Zobacz skrzypce https://jsfiddle.net/69z2wepo/6164/
źródło
Chciałbym przesunąć zmianę uchwytu funkcji i dodać parametr indeksu
do komponentu formularza dynamicznego i przekaż go do komponentu PopulateAtCheckboxes jako rekwizyt. Gdy zapętlasz swoje przedmioty, możesz dołączyć dodatkowy licznik (zwany indeksem w poniższym kodzie), który zostanie przekazany do zmiany uchwytu, jak pokazano poniżej
Wreszcie możesz zadzwonić do nasłuchiwania zmian, jak pokazano poniżej
źródło
Jeśli chcesz zmienić tylko część
Array
, masz komponent reagujący ze stanem ustawionym na.Najlepiej zaktualizować
red-one
wArray
następujący sposób:źródło
newArray
? masz na myślinewItems
? Jeśli tak, czy nie opuściłoby to stanu z tylko jednym przedmiotem później?newItems
dostate
obiektu i nie zaktualizuje istniejącejitems
właściwości.lub jeśli masz dynamicznie generowaną listę i nie znasz indeksu, ale po prostu masz klucz lub identyfikator:
źródło
Spróbuj z kodem:
źródło
Następujący fragment kodu poszedł spokojnie na mój tępy mózg. Usunięcie obiektu i zastąpienie go zaktualizowanym
źródło
Co powiesz na utworzenie kolejnego komponentu (dla obiektu, który musi wejść do tablicy) i przekazania następujących jako rekwizytów?
Tutaj {indeks} można przekazać na podstawie pozycji, w której używany jest ten SubObjectForm.
i setSubObjectData może być mniej więcej taki.
W SubObjectForm, this.props.setData można wywoływać przy zmianie danych, jak podano poniżej.
źródło
źródło
item = Object.assign({}, item, {name: 'newName'});
@ Odpowiedź JonnyBuchanana działa idealnie, ale tylko dla zmiennej stanu tablicy. Jeśli zmienna stanu jest tylko jednym słownikiem, wykonaj następujące czynności:
Możesz zastąpić
[input]
nazwą pola swojego słownika ie.target.value
jego wartością. Ten kod wykonuje zadanie aktualizacji w przypadku zmiany danych wejściowych w moim formularzu.źródło
Wypróbuj to na pewno zadziała, w innym przypadku próbowałem, ale nie działałem
źródło
i Zmień z pola tekstowego dodaj zdarzenie onChange
źródło