当前位置: 动力学知识库 > 问答 > 编程问答 >

reactjs - Facebook React.js : how do you render statefull components on the server ?

问题描述:

I think I'm conceptually missing something with server-side rendering using React.js

Assume I want to create a page to display items from a server-side DB, with an input field

to filter them.

I want a page :

  • that reponds to a URL like /items?name=foobar
  • with a React input field to filter the items by name
  • with a React component to display the list of filtered items

Assume I have a public REST API to query the items on the client side.

Conceptually, what I want to do at first request (GET /items?name=foobar) is :

  • I want my input field to show what the user passed as a Parameter, so I need to

    pass the query parameter ('foobar') to the react component, as a 'prop' (like 'initialName')

So I tried this :

 // A statefull component, maintaining the value of a query field

var ItemForm = React.createClass({

getInitialState : function () {

return {

name : this.props.initialName

};

},

handleInputValueChanged : function() {

var enteredName = this.refs.query.getDOMNode().value.trim();

this.props.onNameChanged(enteredName);

},

render : function () {

return React.DOM.form({

children : [

React.DOM.label({

children : "System name"

}),

React.DOM.input({

ref : "query",

value : this.state.name,

onChange : this.handleInputValueChanged

})

]

});

}

});

  • I also have to do my database query on the server to get a list of items, and

    pass this list of items as a 'prop' of the ItemList (like 'initialItems')

As I understand it, I need a simple component to display the list, receiving it as a 'prop' :

 // A stateless component, displaying a list of item

var ItemList = return React.createClass({

propTypes : {

items : React.PropTypes.array

},

render : function () {

return React.DOM.ul({

children : _.map(this.props.items, function (item) {

return React.DOM.li({

children : [

React.DOM.span({

children : ["Name : ", item.name].join(" ")

})]

});

})

});

}

});

  • Now, I need a component do display the whole Page ; this component will have to maintain the state of the whole page, that is, look at the name entered in the field, and make API queries to update the list items.

    However I don't understand how this component can have an 'initialState' that would work both on the server-side and for later rendering on the client side.

I tried this :

 // A statefull react component, maintaining the list of items

var ItemPage = React.createClass({

getInitialState : function () {

// ?????

// This is where I'm sure the problem lies.

// How can this be known both on server and client side ?

return {

items : this.props.initialItems || []

};

},

queryItems : function (enteredName) {

var self = this;

// The field was emptied, we must clear everything

if (!enteredName) {

this.setState({

items : []

});

} else {

// The field was changed, we want to do a query

// then change the state to trigger a UI update.

// The query code is irrelevant, I think.

doQuery(enteredName).then(function (items) {

self.setState({

items : items

});

});

}

},

render : function () {

// I don't want to display the same view

// if there is no results.

// This uses a 'state' propery of the page

var results = null;

if (_.isEmpty(this.state.items)) {

results = React.DOM.div({

ref : "results",

children : "No results"

});

} else {

results = ItemListView({

// Here items is a 'prop', the ItemList is technically

// stateless

items : this.state.items

});

}

return React.DOM.div({

children : [

ItemForm({

initialName : this.props.initialName,

onNameChanged : this.queryItems

}),

results

]

});

}

});

That's where I'm stuck. I can render things on the server side using something like

var name = // The name from the query parameters

var items = doQueryOnServerSide(name);

React.renderComponentAsString(ItemPage({

initialName : name,

initialItems : items

});

But when I try to do write the client side javascript, what should I do ? I know where I want my dom to be rendered, but what initial props should I pass to the react component ?

React.renderComponent(ItemPage({

initialName : ??? // Read the name parameter from URL ?

initialItems : ??? // I don't know yet, and I don't want to make an API call until the user entered something in the input box

});

Most attempts I have tried ends up with the DOM being 'erased' on the client-side, with this message :

React attempted to use reuse markup in a container but the checksum

was invalid. This generally means that you are using server rendering

and the markup generated on the server was not what the client was

expecting. React injected new markup to compensate which works but you

have lost many of the benefits of server rendering. Instead, figure

out why the markup being generated is different on the client or

server.

Note that only the markup of my ItemList component is erased, so I suppose it's an implementation problem of my component, but I don't really know where to start.

Am I on the right track ? Or do I miss something entirely ?

Thanks

网友答案:

When using server rendering, you should always pass down the same props that you used to render the component on the server. In this case, you need to pass down the same initialItems prop in order for React.renderComponent to pick up the server-rendered markup (by simply JSONifying the props and putting it in the call to renderComponent).

Your general structure of reading from initialItems when specified makes sense to me. Doing so allows you to either make a component with preloaded data or one that has none. What you need to set the initial state to depends on what you want to show in cases where you're rendering a brand-new component from scratch. (If you're always picking up server-rendered markup then you can simply always pass the initialName and initialItems props.)

Hope that makes sense.

分享给朋友:
您可能感兴趣的文章:
随机阅读: