Лекция 25
<div id="app"></div>
<script src="https://unpkg.com/react@16.2.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="https://unpkg.com/babel-polyfill@6.26.0//dist/polyfill.min.js"></script>
<script type="text/babel">
...
</script>
class App extends React.Component {
render() {
return (
<div>
Hello from React!
</div>
);
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
class App extends React.Component {
render() {
return React.createElement(
"div",
null,
"Hello from React!"
);
}
}
ReactDOM.render(
React.createElement(App),
document.getElementById('app')
);
class App extends React.Component {
constructor(props) {
super(props);
this.click = this.click.bind(this);
}
render() {
return <div onClick={this.click}>Hi</div>;
}
click() { console.log(this.props.title); }
}
ReactDOM.render(<App title='Next Facebook!'/>,
document.getElementById('app'));
class App extends React.Component {
constuctor(props) {
super(props);
this.click = this.click.bind(this);
}
render() {
return <div onClick={this.click}>Hi</div>
},
click() {
console.log(this.props.title);
}
});
ReactDOM.render(<App title='Next Facebook!'/>,
document.getElementById('app'));
const h = require('virtual-dom/h');
const diff = require('virtual-dom/diff');
const patch = require('virtual-dom/patch');
const createElement = require('virtual-dom/create-element');
function render(count) {
return h('div', {
style: {
textAlign: 'center',
lineHeight: (100 + count) + 'px',
border: '1px solid red',
width: (100 + count) + 'px',
height: (100 + count) + 'px'
}
}, [String(count)]);
}
let count = 0;
let tree = render(count);
let rootNode = createElement(tree);
document.body.appendChild(rootNode);
setInterval(function () {
count++;
const newTree = render(count);
const patches = diff(tree, newTree);
rootNode = patch(rootNode, patches);
tree = newTree;
}, 1000);
const element = <h1>Hello, world!</h1>;
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
<MyComponent message={'hello world'} />
<MyComponent onClick={this._onClick} />
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
function Hello(props) {
return <h1>Hello, {props.name}</h1>;
}
ReactDOM.render(
<Hello name="Moto"/>,
document.getElementById('app')
);
class Hello extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = { names: ['Rick', 'Carl', 'Negan'],
current: 0 };
}
componentDidMount() {
setInterval(() => { this.setState({
current: (this.state.current + 1) % 3
})}, 1000);
},
render() {
return (<div>Hello
{this.state.names[this.state.current]}!
</div>);
}
});
class ToDoModel {
constructor() {
this.list = [];
}
getItems() {
return this.list;
}
getActiveItems() {
return this.list.filter(x => !x.completed);
}
getCompletedItems() {
return this.list.filter(x => x.completed);
}
...
}
class ToDoModel {
...
getActiveCount() {
return this.list
.filter(x => !x.completed)
.length;
}
getCompletedCount() {
return this.list
.filter(x => x.completed)
.length;
}
...
}
class ToDoModel {
...
addItem(text) {
let item = {
id: Date.now() + this.list.length,
text: text,
completed: false
};
this.list.push(item);
return item;
}
...
}
class ToDoModel {
...
removeItem(id) {
let index = this.list
.findIndex(x => x.id === id);
this.list.splice(index, 1);
}
removeCompleted() {
this.list = this.getActiveItems();
}
...
}
class ToDoModel {
...
updateItem(id, text) {
let index = this.list
.findIndex(x => x.id === id);
this.list[index].text = text;
}
toggleItem(id) {
let index = this.list
.findIndex(x => x.id === id);
this.list[index].completed
= !this.list[index].completed;
}
...
}
class ToDoModel {
...
switchAllTo(completed) {
this.list
.forEach(x => x.completed = completed);
}
}
class NavModel {
constructor() {
this.links = [
{ title: 'All' },
{ title: 'Active' },
{ title: 'Completed' }
];
this.active = this.links[0];
}
...
}
class NavModel {
...
getLinks() {
return this.links;
}
getActive() {
return this.active;
}
setActive(link) {
this.active = link;
}
}
class ToDo extends React.Component {
constructor(props) {
super(props);
this._rerender = this._rerender.bind(this);
this._toggleItem = this._toggleItem.bind(this);
this._toogleAll = this._toogleAll.bind(this);
this._removeItem = this._removeItem.bind(this);
this._addItem = this._addItem.bind(this);
this._updateItem = this._updateItem.bind(this);
this._removeCompleted = this._removeCompleted.bind(this);
this._navigate = this._navigate.bind(this);
this.state = this._getState();
}
...
});
class ToDo extends React.Component {
...
render() {
return (
<div className="todo">
...
</div>
);
}
...
});
<div className="todo">
<div className="todo__title">React ToDo</div>
<Nav links={this.state.links}
activeLink={this.state.activeLink}
navigate={this._navigate}/>
<ToDoSummary remains={this.state.remains}
completed={this.state.completed}/>
<ToDoList tasks={this.state.tasks}
areAllComplete={this.state.areAllCompleted}
toggleItem={this._toggleItem}
toggleAll={this._toogleAll}
removeItem={this._removeItem}
updateItem={this._updateItem}
/>
<ToDoForm addItem={this._addItem}/>
<ToDoClear
removeCompleted={this._removeCompleted}/>
</div>
class ToDo extends React.Component {
...
_rerender() {
this.setState(this._getState());
}
...
});
class ToDo extends React.Component {
...
_getState() {
const state = {
remains: todoModel.getActiveCount(),
completed: todoModel.getCompletedCount(),
links: navModel.getLinks(),
activeLink: navModel.getActive()
};
state.areAllCompleted = state.remains === 0;
...
}
...
});
class ToDo extends React.Component {
...
_getState() {
...
if (state.activeLink.title === 'All') {
state.tasks = todoModel.getItems();
} else if (state.activeLink.title === 'Completed') {
state.tasks = todoModel.getCompletedItems();
} else {
state.tasks = todoModel.getActiveItems();
}
return state;
}
...
});
class ToDo extends React.Component {
...
_toggleItem(id) {
todoModel.toggleItem(id);
this._rerender();
}
_toogleAll() {
todoModel.switchAllTo(!this.state.areAllCompleted);
this._rerender();
}
_removeItem(id) {
todoModel.removeItem(id);
this._rerender();
}
...
});
class ToDo extends React.Component {
...
_addItem(text) {
todoModel.addItem(text);
this._rerender();
}
_updateItem(id, text) {
todoModel.updateItem(id, text);
this._rerender();
}
...
});
class ToDo extends React.Component {
...
_removeCompleted() {
todoModel.removeCompleted();
this._rerender();
}
_navigate(link) {
navModel.setActive(link);
this._rerender();
}
});
class Nav extends React.Component {
render() {
const items = this.props.links.map((link) => {
return (
<NavItem key={link.title}
link={link}
navigate={this.props.navigate}
isActive={link.title
=== this.props.activeLink.title}
/>
)
});
return (
<div className="nav">
{items}
</div>
);
}
});
class NavItem extends React.Component {
constructor(props) {
super(props);
this._navigate = this._navigate.bind(this);
}
...
});
class NavItem extends React.Component {
...
render() {
return (
<div className={'nav__item'
+ (this.props.isActive
? ' nav__item_active' : '')}
onClick={this._navigate}>
{this.props.link.title}
</div>
);
},
_navigate() {
this.props.navigate(this.props.link)
}
});
class ToDoSummary extends React.Component {
render() {
return (
<div className="todo-info">
<span className="todo-info__remains">
{this.props.remains} remains
</span>
{' '}
<span className="todo-info__completed">
/ {this.props.completed} completed
</span>
</div>
);
}
});
class ToDoList extends React.Component {
render() {
const items = this.props.tasks.map((task) => {
return (
<ToDoItem key={task.id}
task={task}
toggleItem={this.props.toggleItem}
removeItem={this.props.removeItem}
updateItem={this.props.updateItem}
/>
);
});
...
}
});
class ToDoList extends React.Component {
render() {
...
return (
<div className="todo__list">
<div className="todo__toggle-all">
<input type="checkbox"
className="todo__checkbox"
checked={this.props.areAllComplete}
onChange={this.props.toggleAll}/>
{' '}
Complete all
</div>
{items}
</div>
);
}
});
class ToDoItem extends React.Component {
constructor(props) {
super(props);
this._edit = this._edit.bind(this);
this._save = this._save.bind(this);
this._toggleItem = this._toggleItem.bind(this);
this._removeItem = this._removeItem.bind(this);
this.state = {
isEditing: false
};
}
...
});
class ToDoItem extends React.Component {
...
_edit() {
this.setState({ isEditing: true });
}
_save(text) {
this.setState({ isEditing: false });
this.props.updateItem(
this.props.task.id,
text
);
}
...
});
class ToDoItem extends React.Component {
...
_toggleItem() {
this.props.toggleItem(this.props.task.id);
}
_removeItem() {
this.props.removeItem(this.props.task.id);
}
...
});
class ToDoItem extends React.Component {
...
render() {
const text = this.state.isEditing ? (
<ToDoTextInput
className="todo__text todo__text_editing"
text={this.props.task.text}
onSave={this._save}/>
)
: (<span className={'todo__text'
+ (this.props.task.completed
? ' todo__text_completed' : '')}
onDoubleClick={this._edit}>
{this.props.task.text}
</span>
);
...
);
}
});
class ToDoItem extends React.Component {
...
render() {
...
return (
<div className="todo__item">
<input type="checkbox"
className="todo__checkbox"
checked={this.props.task.completed}
onChange={this._toggleItem}/>
<span className="todo__destroy"
onClick={this._removeItem}>-</span>
{' '}
{text}
</div>
);
}
});
class ToDoForm extends React.Component {
constructor(props) {
super(props);
this._save = this._save.bind(this);
}
...
});
class ToDoForm extends React.Component {
...
render() {
return (
<div className="todo__form">
<ToDoTextInput
className="todo__text-input"
placeholder="I need to do..."
onSave={this._save}
/>
</div>
);
},
_save(text) {
this.props.addItem(text);
}
});
class ToDoTextInput extends React.Component {
constructor(props) {
super(props);
this._save = this._save.bind(this);
this._onChange = this._onChange.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this.state = {
text: this.props.text ? this.props.text : ''
};
}
...
});
class ToDoTextInput extends React.Component {
...
_save: function() {
this.props.onSave(this.state.text);
this.setState({ text: '' });
}
...
});
class ToDoTextInput extends React.Component {
...
_onChange: function(event) {
this.setState({
text: event.target.value
});
}
_onKeyDown: function(event) {
if (event.keyCode !== 13) return;
this._save();
}
...
});
class ToDoTextInput extends React.Component {
...
render() {
return (
<input className={this.props.className}
placeholder={this.props.placeholder}
value={this.state.text}
onChange={this._onChange}
onKeyDown={this._onKeyDown}/>
);
}
});
class ToDoClear extends React.Component {
render() {
return (
<div className="todo__clear"
onClick={this.props.removeCompleted}>
Clear
</div>
);
}
});
let sleep = todoModel.addItem('Sleep');
todoModel.addItem('Eat');
todoModel.addItem('Code');
todoModel.addItem('Repeat');
todoModel.toggleItem(sleep.id);
ReactDOM.render(
<ToDo/>,
document.getElementById('app')
);
constructor(props)
getDerivedStateFromProps(nextProps, prevState)
componentWillMount() // legacy
render()
componentDidMount()
getDerivedStateFromProps(nextProps, prevState)
componentWillReceiveProps(nextProps) // legacy
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate(nextProps, nextState) // legacy
render()
getSnapshotBeforeUpdate(prevProps, prevState)
componentDidUpdate()
componentWillUnmount()
const App = function (props) {
return <div>{props.title}</div>;
};
ReactDOM.render(
<App title="Next Facebook"/>,
document.getElementById('app')
);
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
if (props.isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
function Mailbox(props) {
return (<div>
<h1>Hello!</h1>
{props.unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length}
unread messages.
</h2>
}
</div>);
}
ReactDOM.render(
<Mailbox unreadMessages={['Hi', 'Re: Hi']} />,
document.getElementById('root')
);
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is
<b>{isLoggedIn ? 'currently' : 'not'}</b>
logged in.
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.logout} />
) : (
<LoginButton onClick={this.login} />
)}
</div>
);
}
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
render() {
return (
<div>
<WarningBanner
warn={this.state.showWarning}
/>
...
</div>
);
}
}
function SplitPane(props) {
return (<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>);
}
function App() {
return (
<SplitPane
left={<Contacts/>} right={<Chat/>} />
);
}
const CommentListWithSubscription =
withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription =
withSubscription(
BlogPost,
(DataSource, props) =>
DataSource.getBlogPost(props.id)
});
function withSubscription(WrappedComponent,
selectData) {
return class {
...
};
}
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
DataSource
.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource
.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
return <WrappedComponent
data={this.state.data}
{...this.props}
/>;
}
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {value: ''};
}
...
}
class NameForm extends React.Component {
...
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert(
'A name was submitted: '
+ this.state.value
);
event.preventDefault();
}
...
}
class NameForm extends React.Component {
...
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.nameRef = React.createRef();
}
...
}
class NameForm extends React.Component {
...
handleSubmit(event) {
alert(
'A name was submitted: '
+ this.nameRef.current.value
);
event.preventDefault();
}
...
}
class NameForm extends React.Component {
...
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
type="text"
ref={this.nameRef}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
...
}
const FancyButton = React.forwardRef(
(props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
)
);
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
class App extends React.Component {
constructor(props) {
super(props);
this.state = { l: 0, r: 0}
}
render() {
return [
<C title="left" value={this.state.l} />,
<C title="right" value={this.state.r} />,
]
}
...
}
class App extends React.Component {
...
componentDidMount() {
setInterval(() => {
const state = { ...this.state };
if ((state.l + state.r) % 2 === 0) {
state.l++;
} else {
state.r++;
}
this.setState(state);
}, 1000);
}
}
const C = props => {
console.log('Rendering C', props);
return (
<div>
{props.title}: {props.value}
</div>
);
}
class C extends React.Component {
shouldComponentUpdate(nextProps) {
return nextProps.value !== this.props.value;
}
render() {
console.log("Rendering C", this.props);
return (
<div>
{this.props.title}: {this.props.value}
</div>
);
}
}
class C extends React.PureComponent {
render() {
console.log("Rendering C", this.props);
return (
<div>
{this.props.title}: {this.props.value}
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
}
render() {
return <C values={this.state.values} />;
}
...
}
class App extends React.Component {
...
componentDidMount() {
setInterval(() => {
const state = { ...this.state };
state.values.push(state.values.length);
this.setState(state);
}, 1000);
}
}
class C extends React.PureComponent {
render() {
console.log("Rendering C", this.props);
return (
<div>
{this.props.values.join(',')}
</div>
);
}
}
// same as PureComponent
const MyComponent = React.memo(function MyComponent(props) {
/* render using props */
});
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
>>>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
>>> 2 изменения, 1 создание
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
>>>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
>>> 1 создание
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
render() {
// React does *not* create a new div.
// It renders the children into `domNode`.
// `domNode` is any valid DOM node,
// regardless of its location in the DOM.
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
...
}
class ErrorBoundary extends React.Component {
...
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// You can also log the error
// to an error reporting service
logErrorToMyService(error, info);
}
...
}
class ErrorBoundary extends React.Component {
...
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.__onClick = this.__onClick.bind(this);
this.state = { value: 0 };
}
...
}
class App extends React.Component {
...
render() {
return (
<div onClick={this.__onClick}>
{this.state.value}
</div>
);
}
__onClick() {
this.setState({ value: this.state.value + 1 });
this.setState({ value: this.state.value + 1 });
}
}
class App extends React.Component {
...
__onClick() {
this.setState(state =>
({ value: state.value + 1 })
);
this.setState(state =>
({ value: state.value + 1 })
);
}
}