React: Using parent to pass data between sibling components - javascript

I need to render a widget which takes in settings from a SettingsPanel and passes them to a LayoutPanel (which would then re-render itself based on the updated settings). I can't seem to figure a 'clean' way to do this. Here's what I have so far:
Widget.js
class Widget extends Component {
handleSettingsChange() {
//need to let layout know of change
}
render() {
<div>
<SettingsPanel
onSettingsChange={handleSettingsChange}
initialSettings={this.props.settings}
/>
<Layout
data={ this.props.data}
/>
</div>
}
}
App.js
const settings = {
hideImages: true,
itemsPerPage: 5
}
<Widget settings={ settings } data={ data } />
My first thought was that, within Widget, I could do
constructor() {
super()
this.setState({ settings: this.props.settings });
}
handleSettingsChange(data) {
//Assuming data is of the form {changedProperty: value}
this.setState({ settings: Object.assign({}, this.state.settings, data) });
}
render() {
<div>
<SettingsPanel
onSettingsChange={handleSettingsChange}
initialSettings={this.props.settings}
/>
<Layout
data={ this.props.data}
settings={ this.state.settings }
/>
</div>
}
This way the parent is a dump message broker, and doesn't not have to know the specifics of what specific settings are being change. But of course, this doesn't work because,
a. I'm told setting state from props is an anti-pattern
b. The constructor doesn't have access to this.props anyway, and doing it else-where (componentDidMount?) feels wrong.
My second thought was to go to setProps to set props on children from the parent, but of course that's deprecated.
How do I solve this? I could probably re-architect this so this isn't a problem, but frankly I'd just like to understand what I'm missing, or what the 'react' way of solving things like this is.
This question is similar to the same problem I'm having but I'm having trouble understanding/applying what the accepted solution suggests to my problem.
tldr; "How can I make a parent component pass state from one child to another, while being ignorant of what exactly it's passing?"

While lukewestby's answer is correct in that it solves the problem in the original question, after further reflection I realized that that was the wrong question to ask - i.e., it was an architecture issue which is why I was having trouble with the implementation.
In this case the data-flow is [data]-> App -> Widget -> Children. "App" fetches data, so App owns it. Widget should not be able to to modify, handle changes to data it doesn't own. Hence, the callback needs to be passed to the grandparent (App), not the parent (Widget). Here's what I ended up doing.
App.js
constructor() {
this.state = { settings: { hideImages: true } };
}
handleSettingsChange(data) {
this.setState({ settings: data });
}
render() {
<Widget settings={ this.state.settings }
onSettingsChange={ this.handleSettingsChange }
/>
}
Widget.js
handleDisplaySettingsChange(data) {
this.props.onSettingsChange(data);
}
render() {
return (
<div>
<SettingsPanel
onSettingsChange={ this.handleDisplaySettingsChange }
/>
<Layout
{...this.props.settings }
/>
</div>
);
}
The advantage of doing it this way is that there is a single source of truth for settings, which is maintained by the owner (App). In the original suggestion, if on a later fetch by App the settings had changed the inner components wouldn't receive it (which explains why the React docs claim setting state from props is an anti-pattern)

It's not necessarily true that setting state from props is an anti-pattern. It seems completely reasonable to initialize a component's state from props in the constructor. Doing so doesn't degrade testability or reliability much if at all.
You can access props in the constructor as they are passed as the first parameter. You'll need to call super first, but then you can do whatever you'd like:
class Widget extends React.Component {
constructor(props) {
super(props);
this.setState({ settings: props.settings });
}
}
Then you can continue down the path you were originally headed. Another slightly more complex option would be to use a state container like Redux to handle state management and transitions for you, but only do this if you don't mind the additional learning and complexity of adding more to your app setup.

Related

Can I put react component inside state?

Can't find any recent official info if any of the three options below is allowed?
constructor(props) {
this.state = {
item: <SomeItem />,
item1: () => <SomeItem />,
item2: SomeItem,
};
}
I found this answer but it references an old link from web archive which says:
What Shouldn’t Go in State?
...
React components: Build them in render()
based on underlying props and state.
But that link doesn't say why that is a bad idea, if it will introduce bugs, etc.
This is a really good question.
The reason that putting components in state is advised against is just that it goes fundamentally against the React model, which is that a component provides a render method (which is a pure function) that the React engine uses to automatically update the DOM to reflect the values of the component's props and state.
The output of that render, i.e. the React Element, is supposed to be used directly by the React engine. The contract is that your app, and all its components, generate a bunch of Elements in a pure way for the React engine to manage.
By doing things like introducing side effects in render, or putting the Elements in state, you're essentially breaking the 'pure' contract and it may give unpredictable results, which may or may not be considered bugs in your application. The specifics of the bugs may even change with different versions of React, with different engine implementations. The point is that you're breaking the React contract, so whilst it may work in some cases, it also may not in others or even the same cases as React itself changes. The behaviour is not guaranteed.
React has built-in ways to cache renders based on prop values, like React.memo, that the engine provides and understands, and are part of the contract. If you want to cache render output for performance reasons, this is the way to do it.
Indeed, this is exactly why such functions are provided by the React API rather than just letting you do it yourself.
At the end of the day, React component instances are just objects, and you can store objects in state, so it shouldn't cause any trouble if you avoid pitfalls. One such pitfall is that if you're creating handlers to put on their props, those handlers will close over the context in which they're created, which may lead to some unexpected outcomes. Here's an example of that:
const {useState, Fragment} = React;
function Thingy({onClick}) {
return <div onClick={onClick}>A</div>;
}
// STALE CLOSURE
function Example() {
const [value, setValue] = useState(0);
const [comp, setComp] = useState(
<Thingy onClick={() => { console.log("A: value = " + value); }} />
);
const handler = () => {
setValue(v => {
++v;
console.log("B: value = " + v);
return v;
});
};
return <Fragment>
{comp}
<div onClick={handler}>B</div>
</Fragment>;
}
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
This is the classic stale closure thing. It's probably a bit easier to do accidentally using functional components and hooks (as I did there) rather than class components, but it's definitely possible to do with class components as well.
But if you're not doing that (either not creating functions for the component you're storing, or creating ones that don't use anything they close over that may change), it should be fine.
But look at React.memo, which may be a better answer depending on what your reason for wanting to put component instances in state is.
You can do something like this, if I understand you right
const Title = () => {
return <h1>Hello CodeSandbox</h1>;
};
class App extends React.Component {
state = {}
constructor(props) {
super(props)
this.state = {
item: function() {
return <Title />;
}
};
}
render() {
return (
<div className="App">
{this.state.item()}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
export default App;
You can do it, but it's a bit strange. A React element is an object like any other. In this case, it will be the result of a method call:
// `<SomeItem/>` compiles to
React.createElement(SomeItem, null);
// A similar `React.createElement("div", null)` becomes
const el = {
$$typeof: Symbol(react.element),
key: null,
props: {},
ref: null,
type: "div",
_owner: null,
}
It's strange because it's unnecessary (and a little confusing). You can just generate the element (including any state or props updates) whenever you need it.
There's also a risk that you break one of the core guarantees of React: elements are immutable. Storing the element like this gives you a chance to mutate it and thus confuse React.
If you need many copies of the same element then it may be slightly more performant to keep it like this, especially if it is expensive to generate.

Updating Parent Component state from multiple child components' componentDidMount() synchronously

Ok so this question is a bit tricky. I have been thinking about whether this is even correct concept wise, considering React is supposed to be a one-way flow of data, from parent to children, and not viceversa. But I would like to post the question anyway so I get different opinions and even possibly a way to get this to work.
In my app, I have a pretty large component that accepts forms as its children, and does some nifty React magic to pass its methods to the children so when the children elements are changed, they trigger the parent components methods that store the data in state and handles the form submissions. It works very nicely, however it is not so good at catching "defaultValues".
In a nutshell, I'm trying to trigger my parent method on the chilren's componentidMount() method, and it works, however, if there's more than one child trying to do this, the method gets called twice but it only uses the second child's dataset.
I have created a simplified version of my issue in the following code:
import React from 'react'
export class Parent extends React.Component {
constructor(props){
super(props)
this.state = {
data : {name:'james'}
}
this.updateData = this.updateData.bind(this)
}
updateData(key,data){
console.log('updating data')
this.setState({
data : {...this.state.data,[key]:data}
})
}
render(){
console.log(this.state)
return (
<div>
<Child1 updateData={this.updateData}/>
<Child2 updateData={this.updateData}/>
</div>
)
}
}
class Child1 extends React.Component {
componentDidMount(){
this.props.updateData('child1','myData')
}
render(){
return (
<div>
I am Child 1
</div>
)
}
}
class Child2 extends React.Component {
componentDidMount(){
this.props.updateData('child2','myData2')
}
render(){
return (
<div>
I am Child 2
</div>
)
}
}
This code will render 'updating data' twice on the console, but it will only update the state with the data sent in child2. Again, I can see how this may not be the best approach considering that im setting the state of a parent from its children, but it would be a good solution for setting default values on a parent component that gets reused a lot with different children.
Im all ears stack overflow
I think the problem is that setState does both updates at the same time (batches them) meaning the same initial state is used when merging both partial states. You need to use updater function as shown by kind user:
this.setState((prevState) => ({ data: { ...prevState.data, [key]: data } }));

State management in React component when the business logic is outside of the component

there! The App component is a container for three different components:
Map renders a map with visual marks on it representing addresses the user has provide.
List component contains all added addresses as list items.
Input allows a user to add a new address (in my terms that is called LocationPoint).
Right now, the App keeps locations array in state with all those addresses (LocationPoints) and passes that array into all child components.
Manipulations with LocationPoints (add/move/update/deleteLocationPoint) are taken out to separate function as they are quite generic and may be reused somewhere else later.
But because those functions do not know about state existence I have to create some kind of "provider" functions that calls those actions (addLocationPoint, deleteLocationPoint, etc). E.g. addLocationPoint func has to be called inside App.addLocationPoint.
The following example should explain what I was talking about better. Note: snippet doesn't work as it's not a real implementation.
// Adds a new location point
const addLocactionPoint = (locations: array, address: string) => {
// ...
return updatedLocations;
}
class App extends React.Component {
constructor() {
this.state = {
locations: [],
}
// bind addLocPoint, etc.
}
addLocPoint(address) {
this.setState(state => {
addLocactionPoint(state.locations, address);
});
}
// ...
render() {
return (
<Input onSubmit={ this.addLocPoint } />
<List
onDrag={ this.moveLocPoint }
onDelete={ this.deleteLocPoint }
/>
<Map data={ this.state.locations } />
);
}
}
Can my approach be considered as a good practice? Or there are other ways to reduce amount of logic in App component and to avoid creating those "providers" without using state management libs (MobX, Redux, etc). Maybe the case I consider is a right time to introduce a Redux or MobX?
I'll be really grateful for advice or recommendations or links to explore on this question.
It's already good enough, this is how global state is usually maintained in vanilla React with no state management libraries. Context API may additionally be used to pass the state to nested components.
The thing that can be changed is that functions that update the state can actually be extracted and used as setState higher-order updater functions:
const addLocationPoint = (address) => ({ location }) => {
// ...
return updatedLocations;
}
class App extends React.Component {
...
addLocPoint(address) {
this.setState(addLocationPoint(address));
}
...
A similar idea is used in Redux action creators.
In order for state updaters to provide real improvements, they have to be moved to another module. In this case they can be tested separately from a component that uses them and mocked with jest.mock in a component that uses them.

React - proper state management for rows of unmounted JSX?

We have a crazy DOM hierarchy, and we've been passing JSX in props rather than embedding children. We want the base class to manage which documents of children are shown, and which children are docked or affixed to the top of their associated document's window.
List (crazy physics writes inline styles to base class wrappers)
Custom Form (passes rows of JSX to Base class)
Base Class (connects to list)
Custom Form (passes rows of JSX to base class)
Base class (connects to list)
The problem is that we're passing deeply nested JSX, and state management / accessing refs in the form is a nightmare.
I don't want to re-declare every row each time, because those rows have additional state attached to them in the Base Class, and the Base Class needs to know which rows actually changed. This is pretty easy if I don't redeclare the rows.
I don't know how to actually deal with rows of JSX in Custom Form.
Refs can only be appended in a subroutine of render(). What if CustomForm wants to measure a JSX element or write inline CSS? How could that JSX element exist in CustomForm.state, but also have a ref? I could cloneElement and keep a virtual DOM (with refs) inside of CustomForm, or depend on the base class to feed the deeply-nested, mounted ref back.
I believe it's bad practice to write component state from existing state. If CustomForm state changes, and I want to change which rows are passed to BaseClass, I have to throttle with shouldComponentUpdate, re-declare that stage document (maintaining row object references), then call setState on the overarching collection. this.state.stages.content[3].jsx is the only thing that changed, but I have to iterate through every row in every stage document in BaseClass when it sees that props.stages changed.
Is there some trick to dealing with collections of JSX? Am I doing something wrong? This all seems overly-complicated, and I would rather not worsen the problem by following some anti-pattern.
Custom Form:
render () {
return <BaseClass stages={this.stages()}/>
}
stages () {
if (!this._stages) this._stages = { title: this.title(), content: this.content() };
return this._stages;
}
title () {
return [{
canBeDocked: false,
jsx: (
<div>A title document row</div>
)
}
}
content () {
return [{
canBeDocked: false,
jsx: (
<div>Hello World</div>
)
}, {
canBeDocked: true,
jsx: (
<div>Yay</div>
)
}
}
What I usually do is just connect the lower level components via Redux. This helps with not passing the state in huge chunks from the top-most component.
A great video course by one of the React creators, Dan Abramov: Getting started with Redux
Absolutely agree with #t1gor. The answer for us was to use REDUX. It changed the entire game for us. Suddenly a button that is nested 10 levels deep (that is, inside a main view, header, header-container, left side grid, etc, etc, deeper and deeper) into purely custom components, has a chance to grab state whenever it needs.
Instead of...
Parent (pass down state) - owns state vars
Child (will pass down again) - parent has state vars
Grandchild (will pass down a third time) - grandparent has state vars
Great Grandchild (needs that state var) - great grandparent has state vars
You can do...
Parent (no passing) - reads global state vars
Child
Grandchild
Great Grandchild - also reads same global level state vars without being passed...
Usually the code looks something like this...
'use strict'
//Importation of Connection Tools & View
import { connect } from 'react-redux';
import AppView from './AppView';
//Mapping -----------------------------------
const mapStateToProps = state => {
return {
someStateVar: state.something.capturedInState,
};
}
const mapDispatchToProps = dispatch => {
return {
customFunctionsYouCreate: () => {
//do something!
//In your view component, access this by calling this.props.customFunctionsYouCreate
},
};
}
//Send Mappings to View...
export default connect(mapStateToProps, mapDispatchToProps)(AppView);
Long story short, you can keep all global app state level items in something called a store and whenever even the tiniest component needs something from app state, it can get it as the view is being built instead of passing.
The issue is having content as follows, and for some reason not being able to effectively persist the child instances that haven't changed (without re-writing the entire templateForChild).
constructor (props) {
super(props);
// --- can't include refs --->
// --- not subroutine of render --->
this.state = {
templateForChild: [
<SomeComponentInstance className='hello' />,
<AnotherComponentInstance className='world' />,
],
};
}
componentDidMount () {
this.setState({
templateForChild: [ <div className='sometimes' /> ],
}); // no refs for additional managing in this class
}
render () {
return ( <OtherManagerComponent content={this.state.templateForChild} /> );
}
I believe the answer could be to include a ref callback function, rather than a string, as mentioned by Dan Abramov, though I'm not yet sure if React does still throw a warning. This would ensure that both CustomForm and BaseClass are assigned the same ref instance (when props.ref callback is executed)
The answer is to probably use a key or createFragment. An unrelated article that addresses a re-mounting problem. Not sure if the fragment still includes the same instances, but the article does read that way. This is likely a purpose of key, as opposed to ref, which is for finding a DOM node (albeit findDOMNode(ref) if !(ref instanceof HTMLElement).

_React Js one page application

I am looking to create a one page application with ReactJS.
Is it advisable to combine it with angular or it is suitable just on its own? I would like to populate the one page site with sections - adding various features like carousels, sliders, isotope filters ...
<!DOCTYPE html>
<html>
<head>
<title>React Js one page</title>
<script src="https://fb.me/react-with-addons-0.14.7.min.js"></script>
<script src="https://fb.me/react-dom-0.14.7.min.js"></script>
</head>
<body>
<section>
One
<script>
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="Colonel Mustard" />, mountNode);
</script>
</section>
<section>
Two
<script>
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
ReactDOM.render(<CommentBox />, mountNode);
</script>
</section>
<section>
Three
<script>
"use strict";
var MarkdownEditor = React.createClass({
displayName: "MarkdownEditor",
getInitialState: function getInitialState() {
return { value: 'Type some *markdown* here!' };
},
handleChange: function handleChange() {
this.setState({ value: this.refs.textarea.value });
},
rawMarkup: function rawMarkup() {
return { __html: marked(this.state.value, { sanitize: true }) };
},
render: function render() {
return React.createElement(
"div",
{ className: "MarkdownEditor" },
React.createElement(
"h3",
null,
"Input"
),
React.createElement("textarea", {
onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value }),
React.createElement(
"h3",
null,
"Output"
),
React.createElement("div", {
className: "content",
dangerouslySetInnerHTML: this.rawMarkup()
})
);
}
});
ReactDOM.render(React.createElement(MarkdownEditor, null), mountNode);
</script>
</section>
</body>
</html>
If you're just starting out with React, I'd highly recommend following Pete Hunt's advice:
You’ll know when you need Flux. If you aren’t sure if you need it, you don’t need it.
The best thing to do is get started with React by itself and manage application state using the local state that comes with each of your components.
When you find that you start having to pass data back up to parent components, then add Flux into the mix and rewrite your stateful components to instead use Flux stores.
We'll look at how to tackle writing a simplified version of the StackOverflow answer component as a React application from the bottom up.
Notice, I said React application, not React component. This is because there's no technical difference. A React application is a big React component made up of lots of smaller ones.
Identify Components
Once you have an interface for your application (anything from wireframes to html/css) you can visually subdivide them to work out how they'll fit together as React components.
There are no hard and fast rules about how exactly you decide what should or should not be it's own component, but you'll get a feeling for it the more times you do it.
is <Answer />
is <Votes />
is <AnswerText />
is <AnswerActions />
Because we're building from the bottom up, we'd start by implementing each of the child components and testing that they work alone.
At this point in the development lifecycle we'd just write static markup for each component. There's no need to think about props or state yet.
We can use the stateless component syntax to get started on the components we've identified. Here's an example of how we might write the <Votes /> component.
function Votes() {
return (
<div>
<a>▲</a>
<strong>0</strong>
<a>▼</a>
</div>
);
}
Of course this doesn't do anything, but it allows us to start composing our components to get a feel for the structure of the application.
We can render this into the DOM to check that it works at any time.
ReactDOM.render(<Votes />, document.getElementById('app'));
Once you'd finished implementing static versions of the other components, you could put them together to create the parent <Answer /> component.
function Answer() {
return (
<div>
<Votes />
<AnswerText />
<AnswerActions />
</div>
);
}
Design Data Flow
The next thing to do is to figure out how data flows through your application.
At this point we can create some dummy data in the form of an answer object that looks something like this:
{
"id": 0,
"votes": 0,
"text": "This is an answer"
}
Initially we can render the <Answer /> component by passing this answer object to it as a prop.
<Answer answer={answer} />
Now it's that components job to pass down the appropriate data to its children.
Obviously not each child needs all of the data though, so we'll have to decide what data goes where. Let's update our <Answer /> component.
function Answer(props) {
var answer = props.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
The <Votes /> component needs know the current number of votes and it also needs to know the id of the answer so that it can communicate change to the server.
The <AnswerText /> component just renders a block of text, so that's all we need to pass it.
Finally, the <AnswerActions /> component renders a list of links that allow the user to perform some action (share, edit, flag) on the answer. This component also needs the answer's id so that it can communicate with the server.
Now we have to update these child components in turn to use these new dynamic values, instead of the static values we used at first. We'll revisit the <Votes /> component to see this happen.
function Votes(props) {
var urls = {
upvote: '/api/answers/' + props.id + '/upvote',
downvote: '/api/answers/' + props.id + '/downvote'
};
return (
<div>
<a href={urls.upvote}>▲</a>
<strong>{props.votes}</strong>
<a href={urls.downvote}>▼</a>
</div>
);
}
Now our vote component will make a HTTP request to the appropriate endpoint when we click on the vote buttons, however, we'd rather make this update without reloading and re-rendering the entire application.
Identify Stateful Components
The final piece of the component development process is to identify stateful components. These components have moving parts and data that will change during the lifetime of the application.
Each time the state inside a component changes, the entire component re-renders. We can revisit the wireframes to see which of our components needs to manage changing data.
This application only has one stateful component () and that's `. When we click on one of the arrows, we need to update the number to reflect the new count.
It's the only one of our components that ever needs to re-render.
This means we'll need to upgrade the component to use React's createClass syntax. This allows it to start managing it's own state.
var Votes = React.createClass({
getInitialState: function() {
return { votes: this.props.votes };
},
upvote: function() {
var newVotes = this.state.votes + 1;
this.setState({
votes: newVotes
});
},
downvote: function() {
var newVotes = this.state.votes - 1;
this.setState({
votes: newVotes
});
},
render: function() {
return (
<div>
<a onClick={this.upvote}>▲</a>
<strong>{this.state.votes}</strong>
<a onClick={this.downvote}>▼</a>
</div>
);
}
});
I've jumped the gun a bit and implemented the full component, but hopefully you'll get the idea.
First we use getInitialState to set up some state to represent the initial number of votes in the component.
Next we implement upvote and downvote component methods that update the component state.
Finally we re-implement the render method from before, but have the arrows trigger the new component methods, not page requests.
Each time we make a call to setState, React will re-render the component. Hopefully you can see why we put the state in the <Votes /> component and not the <Answer /> component. It would be crazy to re-render the answer text and actions, just because the votes had changed.
Flux It Up
Once we've identified and implemented all of our stateful components, we can start to move their state out into Flux stores.
It's much more likely that a real application would have an <AnswerStore /> than a <VoteStore />, so that's what we'll implement. For now we'll just keep mocking our data.
var AnswerStore = {
_listeners: [],
_answers: {
"0": {
"id": 0,
"votes": 0,
"text": "This is an answer"
}
},
get: function(id) {
return this._answers[id];
},
update: function(id, update) {
var answer = this.get(id);
var updatedAnswer = Object.assign({}, answer, update);
this._answers[id] = updatedAnswer;
this.emit();
},
listen: function(f) {
this._listeners.push(f);
},
emit: function() {
this._listeners.forEach(function(f) {
f();
});
}
};
In this example, I've written a fairly generic store that contains data, provides simple handlers for listening to model changes and finally exposes methods for mutating the data in the store.
It's important that our update method treats the individual answers as immutable in this application, otherwise we risk mutating an object that other parts of the application have a reference to, causing the object to change underneath them. We use Object.assign to create a new object each time, based on the old one.
The next thing we need to do is set up some action handlers for this store.
dispatcher.register(function(action) {
switch(action.type) {
case 'UPVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes + 1 });
break;
case 'DOWNVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes - 1 });
break;
}
});
This simply wires the update method to two actions called 'UPVOTE' and 'DOWNVOTE'
Now we connect Flux to our <AnswerComponent /> which needs to be re-written in the long form.
var Answer = React.createClass({
getInitialState: function() {
return { answer: {} };
},
componentWillMount: function() {
this.update();
AnswerStore.listen(this.update);
},
update: function() {
var id = this.props.id;
this.setState({ answer: AnswerStore.get(id) });
},
render: function() {
var answer = this.state.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
});
In our componentWillMount method we fetch our initial data for the store, then set up a listener on the store that fetches and updates the component state, whenever the store changes.
Finally, we need a way to dispatch the appropriate actions from our <Votes /> component.
The most popular way to do this is with action creators. An action creator is a function which takes some data as parameters, then packages it up and dispatches it as an action.
var Actions = {
upvote: function(id) {
dispatcher.dispatch({
type: 'UPVOTE',
id: id
});
},
downvote: function(id) {
dispatcher.dispatch({
type: 'DOWNVOTE',
id: id
});
}
};
Then we call these actions from inside our <Votes /> component (which can become stateless again).
function Votes(props) {
var id = props.id;
return (
<div>
<a onClick={Actions.upvote.bind(null, id)}>▲</a>
<strong>{props.votes}</strong>
<a onClick={Actions.downvote.bind(null, id)}>▼</a>
</div>
);
}
This component now uses the action creators to dispatch actions for our Flux store(s) to handle.
If we look at the flow of data through our application, we can see that we now have a unidirectional cycle, rather than a tree.
The <Answer /> component passes the id down to the <Votes /> component.
The <Votes /> component dispatches an action using that id.
The AnswerStore processes the action and emits a change.
The <Answer /> component hears the update and updates its state, re-rendering its children.
Here's a jsfiddle of this demo application.
Scale Up
This is a very simple component that only handles a tiny amount of data flow and even less application state, however, it's enough to show you how to compose a React component and that's all you need to build a React app.
Let's imagine we were implementing a StackOverflow question as a React application.
function App() {
return (
<Page>
<Navigation />
<SideBar>
<MetaDetails />
<Ads />
<RelatedQuestions />
</SideBar>
<Question />
<AnswerList />
<AnswerEditor />
<Footer />
</Page>
);
}
It might seem like a complex application, but you can break it down and express it as distinct components, then you can implement and test the components individually, just like we did here and bring them altogether to form a complex application.
Don't Over Complicate
For most simple React applications like this one, Flux is not actually necessary. It's worth remembering that React was released over a year before Flux and yet it was adopted by a lot of developers regardless.
Of course, I've only really covered structuring and implementing components here. Taking an application from wireframes to deployment is a much more complicated process and there's no way it could be covered in detail in one answer. In time, you'll probably also want to learn about:
Package management and dependencies
Module bundlers
Routing
Hot Reloading
ES6/Babel
Redux
Server Side Rendering
Immutable Data
Inline Styles
Relay/Falcor/GraphQL
It takes some time to get through this list of things, the trick is not to rush. Don't overcomplicate your existing project until you find the reasons that these solutions exist, naturally.
I think react-howto is the best guide out there. Although it's not heavy on detail, it links to a lot of good resources and most importantly it provides an opinionated guide to the order in which you should learn these technologies on your way to becoming a competent ReactJS developer.
The choice of framework (Angular/React) does not prevent building any of the functionality you described, and your site could be built with neither, either, or both of those frameworks.
While you certainly could combine Angular and React, I'm not sure why you would want to, and it's not going to be the easiest of tasks. It's doable, of course, but it's going to provide a lot of difficulty for very little ultimate gain.
If you want to build a SPA with React, I would focus more on finding a Flux implementation that you like, and learning how to integrate that. Flux is designed specifically with React in mind to handle large SPAs which might have complicated data flow, and it's certainly a tool that is easier to incorporate early on.
The only other library I would consider out of the gate would be Immutable.js, which pairs very well with both React and Flux.
But otherwise, until you find a need to bring in additional frameworks/libraries, attempt to hold off. With all of the exciting JS frameworks out there, it's tempting to want to use them all, but in reality you're better off picking one to focus on, and then maybe bringing in some tools from another later on when they are necessitated.

Categories