f(model) -> UI
class HelloMessage extends React.Component {
render() {
return <div>
<input value={this.state.greeting} ref="in"
onChange={(event) => this.updateModel(event)} />
{this.state.greeting}, World
<button onClick={() => this.reset()}>Clear</button>
</div>;
}
constructor(props) {
this.state = {greeting: this.props.greeting};
}
updateModel(event) {
this.setState({greeting: event.target.value});
}
reset() {
this.setState({greeting: ""});
this.refs.in.focus();
}
}
// index.html
<body>
</body>
// main.js
import HelloMessage from './HelloMessage';
var mountNode = document.getElementById('mount');
ReactDOM.render(<HelloMessage greeting="Hello"/>, mountNode);
Run
Copyright 2016, embarc
Copyright 2016, embarc
import { Provider } from 'react-redux';
import store from './store';
import HelloMessage from './HelloMessage';
const mountNode = document.getElementById('mount');
ReactDOM.render(
<Provider store={store}>
<HelloMessage />
</Provider>,
mountNode
);
class HelloMessage extends React.Component {
render() {
const { greeting, updateGreeting, resetGreeting } = this.props;
return
<input onChange={event => updateGreeting(event.target.value)}
value={greeting}/>
{greeting}, World
<button
onClick={() => resetGreeting()}>
Clear
;
}
}
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from './actions';
export default connect(
state => ({
greeting: state.greeting
}),
dispatch => bindActionCreators(Actions, dispatch)
)(HelloMessage);
// Action
export const UPDATE_GREETING = 'UPDATE_GREETING';
export const RESET_GREETING = 'RESET_GREETING';
// Action creator
export function updateGreeting(greeting) {
return {
type: UPDATE_GREETING,
greeting
};
}
export function resetGreeting() {
return {
type: RESET_GREETING
};
}
// store
export default createStore(combineReducers({
greeting: greetingReducer
}));
// reducer
import {UPDATE_GREETING, RESET_GREETING} from './actions';
function greetingReducer(state = 'Hello', action) {
switch (action.type) {
case UPDATE_GREETING:
return action.greeting;
case RESET_GREETING:
return '';
default:
return state;
}
}
All application code can be shared between Server and Client
const server = new Hapi.Server();
server.route({
method: 'GET',
path: '/',
handler: renderRoute
});
function renderRoute(request, reply) {
const html =
ReatDom.renderToString(
<Provider store={store}>
<HelloMessage />
</Provider>
);
// redux offers complete state from single store
reply(renderFullPage(html, store.getState()));
};
<!-- server -->
function renderFullPage(html, initialData) {
return `
<html>
<body>
<div id="mount">${html}</div>
</body>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialData)};
</script>
</html>
`;
}
// client: init store from complete state
const initialState = window.__INITIAL_STATE__;
const store = createStore(..., initialState);
When Checksums on Server and Client side match, React does not re-render
const element = <HelloMessage greeting="Hello"/>;
ReactDOM.render(element, mountNode);
const element = 'just a string'; // type is interred as string
ReactDOM.render(element, mountNode);
src/step-1-typed/client/main.js:12
12: ReactDOM.render(element, mountNode);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call of method `render`
12: ReactDOM.render(element, mountNode);
^^^^^^^ string. This type is incompatible with
237: element: React$Element<Config>,
^^^^^^^^^^^^^^^^^^^^^ React$Element.
See lib: /private/tmp/flow/flowlib_1ddc81a6/react.js:237
type State = {
greeting: string;
};
type Props = {
// ERROR: trying to assign this boolean to state
// greeting: boolean
greeting: string
};
class HelloMessage extends React.Component<any, Props, State> {
constructor(props: Props) {
super(props);
this.state = {greeting: this.props.greeting};
}
}
type GreetingAction = {
actionType: 'UPDATE_GREETING' | 'RESET_GREETING';
payload: ?string;
}
// alternative, more specific declaration
type GreetingAction = {actionType: 'UPDATE_GREETING', payload: string}
| {actionType: 'UPDATE_GREETING' };
}
// action creator
function updateGreeting(greeting: string): GreetingAction {
return {
// must be 'UPDATE_GREETING' or 'RESET_GREETING'
actionType: 'UPDATE_GREETING',
// must be a string or null/undefined
payload: greeting
};
}