javascript - React self-modifying component with proper state/props -
i'm working on pet project right , decided use react view. i'm far developing i'm starting realize might not understand how use react because seems in order simple things have go extreme measures!
so, time arbitrary example! here's code working on jsfiddle
var name = react.createclass({ render: function() { return <button onclick={this.props.onclick.bind(null, this.props.id, 'john')}>{this.props.value}</button> } }); var namecontainer = react.createclass({ getinitialstate: function() { return { names: [ { id: 1, name: 'fred' }, { id: 2, name: 'george' } ] }; }, handleclick: function(id, newname) { names = this.state.names; names.map(function(obj){ if (obj.id === id) { obj.name = newname; } return obj; }); this.setstate({ names: names }); }, render: function() { var handleclick = this.handleclick; var children = this.state.names.map(function(obj){ return <name key={obj.id} id={obj.id} value={obj.name} onclick={handleclick} />; }); return ( <div> {children} </div> ); } });
the idea i'm trying make component can modify itself. in example modification having component able change name original value "john".
why complicated?
it seems made much simpler setting state in name components , letting them modify themselves. unfortunately, far can tell way setting initial state based on props violates "props in getinitialstate anti-pattern" thing.
why not make components manually?
in real application data coming server, of data must fetched @ once. why used getinitialstate pre-load data instead of creating multiple name components manually. also, in react docs state should stored in common parent component, code follows pattern.
things start complex
the problems start when need modify state. since comes in form of array of objects (very typical serializing data server), can't grab specific object corresponds component you're dealing with. far can tell way know pass id around child elements can pass when event handlers fired identify themselves. though, still have loop through each element in state find 1 you're trying modify.
the complexity intensifies...
worse yet, appears can't access key property inside component has property declared on it, in order have information need have double-set value (key , id in example). red flags went me since seems way accomplish goal while not breaking pattern bypass part of system..
another thing i'm annoyed have pass event handler way state-holding object child object. in example that's pretty simple in actual application relation four levels deep. seems me these intermediate steps increase likelihood of introducing bugs missing step in props passing chain.
so answer?
is how react is? there massive shortcut i'm ignoring? how can done simpler while still staying in constraints i've specified?
there are, think, couple of issues example makes more complex necessary: name component leaky abstraction, , name component knows much. (these bit intertwined.)
before tackling these issues, want take small detour discuss approach designing components. find mixing top-down , bottom-up approaches useful in building react applications. using top-down helps identify components build starting idea of component , identifying subcomponents should be, identifying subcomponents of subcomponents, ad nauseam. after identifying components, it's time switch bottom-up approach, take basic component (no new subcomponents required build it) , build self-contained , independent possible, , pick next basic component , implement it, , repeat until done.
when implementing component, can think of incoming props api other components use, , want keep focused possible on functionality component should provide.
so issues in example.
the first name component bit of leaky abstraction. (yes, know arbitrary example, bear me.) component presents onclick part of api (since it's required prop). problematic because tells components want use it, "hey, i've got button in here!" that's fine current implementation of name, happens when need change name have other behavior? onclick might not make sense anymore, , if want change prop name, change ripples throughout rest of code. instead, might consider using name 'update', seems reasonable operation name component while hiding fact contains button internally. sure, can looked @ "take care when naming things" argument, think naming valuable tool in building components both easy use , easy modify later.
the other problem name component knows parent implementation, making harder use in other components later. instead of running 'bind' in name component, let's assume owning container passes function takes single parameter - newname. inside name component, have call function new name want. how affects other state not name's concern, , you've deferred addressing problem until have no other choice. name component looks like:
var name = react.createclass({ render: function() { return <button onclick={() => this.props.update('john')}>{this.props.name}</button>; } });
notice no longer need id; accept , update name.
since have worry updating names in namecontainer component (since that's data is), can worry there. avoid looping on ids, let's create function takes name object , new name, , bind each name object function, similar had in name component. (what's left function takes single parameter newname, can use update function name!). code looks like:
var namecontainer = react.createclass({ getinitialstate: function() { return { names: [ { id: 1, name: 'fred' }, { id: 2, name: 'george' } ] }; }, update: function(obj, newname) { obj.name = newname; this.setstate({ names: this.state.names }); }, render: function() { var children = this.state.names.map(function(obj){ return <name key={obj.id} name={obj.name} update={this.update.bind(null, obj)} />; }, this); return ( <div> {children} </div> ); } });
this simplifies components, , name can used in wider variety of contexts. have updated sample changes described here, , kicks i've added different implementation of name here. namecontainer didn't have change since name api didn't change, though implementation of name pretty different between two.
using bottom-up approach helps deal concern passing props through chain of subcomponents. build , test component, have concerned specific problem addressing component; ensuring pass appropriate props subcomponents. since focused on specific problem, less miss passing crucial piece of data around.
you may @ , still wonder "why complicated?". consider application want show same data in different ways simultaneously (e.g., graph , corresponding table of data). react gives way share data across components , keep them in sync. if held data , manipulated them locally possible (as far down subcomponent chains possible), have implement synchronization between components yourself. placing data high in component chain possible, make (effectively) global components, , react can worry rest of details. worry passing data , how render data, , react takes care of rest.
as final note, if application continues grow in complexity, might consider taking @ flux. architectural pattern helps manage application complexity. it's no panacea, found pretty when used on moderately complex apps.
Comments
Post a Comment