It is a common practice to pass in the form of a prop, from a root component A, to a subcomponent B, a function that will change the state of A. Like so:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'foo'
};
this.handleNameChange = this.handleNameChange.bind(this);
}
render() {
return (<NameChanger name={this.state.name} onNameChange={this.handleNameChange} />)
}
handleNameChange: function(newName) {
this.setState({
name: newName
});
}
}
Now as you can see NameChanger is one level down only so not a big issue there. But what if it had been down 3 or even 4 levels? We would have had to pass it down the chain of components and that bothers me big time. Is there a way to make a function globally available within the app?
I looked at Context (https://reactjs.org/docs/context.html) but I am not sure it is the right design choice for globally available functions. Or is it?
Thanks
In a typical React application, data is passed top-down (parent to
child) via props, but this can be cumbersome for certain types of
props (e.g. locale preference, UI theme) that are required by many
components within an application. Context provides a way to share
values like these between components without having to explicitly pass
a prop through every level of the tree.
https://reactjs.org/docs/context.html
Try using Redux or Mobx(very easy to start with) as state management library to solve this problem.
Related
I'm new at React/React Native and I just began working on a company and, thus, working on existing projects.
The thing is, I can't understand this Creation Lifecycle Hooks on the begining of this code:
export default class ListResult extends React.Component {
constructor(props) {
super(props);
this.state.answers = props.navigation.state.params.answers;
this.state.saving = props.navigation.state.params.saving;
this.state.loading = false;
}
state = {
answers: null,
saving: false,
loading: true,
location: null,
dialogLocation: false,
latitude: '',
longitude: '',
};
location = null;
latitude = '';
longitude = '';
}
Can someone please provide me some explanation why is he using the constructor to initialize the state, and after that, used state to define variables, and then, after that, he also set some values to those variables?
Its a common mistake and even emphasised in related docs:
Avoid copying props into state! (in the constructor) This is a common mistake.
Only use this pattern if you intentionally want to ignore prop updates.
This code roughly should fixed to:
class ListResult extends React.Component {
state = {
answers: null,
...
};
componentDidMount = () => {
const { answers, saving } = props.navigation.state.params;
this.setState({ answers, saving });
};
}
As for explanation, he uses both class instances and a constructor (why?), in "modern" React, with class components you shouldn't use the constructor (instead, use class properties).
When a class is defined, the class instances evaluated first and then the constructor called, thats why he overrides the initial values in their instances.
Without answering the question in details I try to highlight the difference of react lifecycle thinking versus the conventional JS DOM manipulation.
If you work with React you manipulate the so called "virtual dom" with your JS code and not the real DOM.
How virtual dom works in nutshell: if ANY prop or state is changing of any component in the dom tree the whole child dom tree gets re-rendered. Lifecycles means how do you handle these continuous re-renderings within the components.
But no worries. It does not mean the whole DOM gets re-rendered every time. On top of this virtual dom there is a guy called ReactDOM which continuously compares the virtual-dom states and do the necessary changes only in the real DOM.
What's the correct way to implement the below design?
<Parent with state>
<connected element with Store configuration dependant on parent>
<child of both, dependant on store/connected element>
</connected element>
</parent>
I'm not sure how much code to include and there's almost certainly more than I need so here's a snippet that I think explains what I'm trying to accomplish.
class SceneOne extends React.Component {
constructor (props) {
super(props);
this.state = {
opacity: 0,
script: sceneOneScript
};
}
render () {
return (
<ScriptReader script = {this.state.script}> //This is connected and creates a store from the script passed via state.
<Screen data-image="caves.png" data-opacity={this.state.opacity} >//This uses actual SceneOne.state.opacity which is updated to 1 after a delay in ComponentDidMount
<ConditionalTitle props = {this.props}/> //This needs the store.
</Screen>
</ScriptReader>
);
}
}
I'm really hoping that I don't need to connect <ConditionalTitle> because that feels like it breaks agnostic components principles. I'm also hoping that I don't need to install <ConditionalTitle> inside the definition of <ScriptReader> because I'm planning on reusing it and passing different children/scripts etc.
ie. there'll be a that has a <ScreenReader> child and it may not have a title, or may have elements that aren't required in <SceneOne>.
use a HOC inside the definition of the ScriptReader that composes all of the options
I want to use Chart.js on my website. As you can see title, I'm using React.js. To use Chart.js, I need the canvas and context like this:
let context = document.getElementById('canvas').getContext('2d');
let chart = new Chart(context, ...);
so I design the component like this:
export function updateChart() {
let context = this.refs.chart.getContext('2d');
let chart = new Chart(context ,... );
...
}
export default class GraphChart extends React.Component {
constructor() {
super();
updateChart = updateChart.bind(this);
}
componentDidMount() {
updateChart();
}
render() {
return <canvas ref="chart" className="chart"></canvas>;
}
}
as you can see, I exported two things, update chart function and GraphChart class. Both will using in parent component like this:
import { updateChart } from './GraphChart';
import GraphChart from './GraphChart';
class Graph extends React.Component {
...
someKindOfAction() {
// update chart from here!
updateChart();
}
render() {
return (
<div>
<SomeOtherComponents />
<GraphChart />
</div>
);
}
}
then Parent class using exported updateChart function to update chart directly. It was working, but only first time. After unmount and mount the GraphChart component, it's refs are just empty.
Why refs is empty? And If I did wrong way, how can I get canvas context for initialize Chart.js?
Object refs is undefined, because this is not what you think it is. Try logging it.
The function you’re exporting is not bound to this of your component. Or perhaps it is, but to the last created instance of your component. You can never be sure that’s the mounted instance. And even if you are, you can not use multiple instances at the same time. So, I would dismiss this approach entirely.
Other than that, providing the function to alter some component’s state is exactly the opposite of what’s React is trying to accomplish. The very basic idea is that the component should know to render itself given some properties.
The problem you are trying to solve lies in the nature of Canvas API, which is procedural. Your goal is to bridge the gap between declarative (React) and procedural (Canvas) code.
There are some libraries which do exactly that. Have you tried react-chartjs? https://github.com/reactjs/react-chartjs
Anyways, if you’re wondering how the hell should you implement it the “React way”, the key is to declare properties your component handles (not necessarily, but preferably), and then to use component lifecycle methods (e.g. componentWillReceiveProps and others) to detect when properties change and act accordingly (perform changes to the canvas).
Hope this helps! Good luck!
We're currently building a React-Redux frontend with a REST API backend powered by Node. I'm unsure about whether to use a Redux or a simple call to the API on mounting the component.
The component is a simple list of profiles which are going to be displayed throughout (but not constantly) the site.
Sorry for asking this. Maybe there's something to read through available?
I would advice you to take a look at two things:
1) The first React tutorial on Facebook is very underrated:
https://facebook.github.io/react/docs/thinking-in-react.html
It exposes a very clear way to think about how to think about the tree structure of your views.
2) From there, move to reading about Containers and Components:
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
This post explains that React components too often do two things: act as renderers and as controllers (taking on both the V and the C on MVC).
Now, what your React view needs is a controller. Fetching it whenever you mount the component overlaps two different concerns: how to display the information and how to fetch it.
You could do it with a single, bigger React component that manages the complete state of your application:
class MyApp extends React.Component {
componentDidMount() {
fetch('/profiles').then(res => res.json().then(::this.setState))
}
render() {
if (this.state) {
return <ProfileList profiles={this.state} />
} else {
return <span>Loading...</span>
}
}
}
That would be your "Container". Your "Component" is a pure representation of the list of profiles, that needs not care about how that information was retrieved:
class ProfileList extends React.Component {
render() {
return <ul>
{
this.props.profiles.map(
profile => <li key={profile.id}>{profile.name}</li>
)
}
</ul>
}
}
Redux is just another way of doing this that enables better reuse of information, and makes that same information available to different components (hiding the instance of the "store" as a mixin). That MyApp class on top of your structure serves a similar function to the Provider class in redux: allowing child components to access information needed to display themselves.
I recently watched a talk by David Nolen where he says that 'immutability is an implementation detail in React'?
What does this mean and if this wasn't the case, how would React be different?
What does "implementation detail" mean:
I would summarize as:
Immutability is a detail of react that you have to implement yourself.
BTW: "Detail" is this case can still mean a lot of work.
React depends on props and state to be immutable.
React does not make props or state immutable for you. You have to ensure that in your code yourself.
So the following code is a recipe for disaster:
// DO NOT TRY THIS AT HOME
var customerObject = { name: "Bill" };
this.setState( customer: customerObject }; // valid react code, triggering re-render
...
customerObject.name = "Karl";
// state still has the same customerObject,
// but the contents of the object have changed. This is where things break down.
React has to ensure that its internal virtual DOM, and all props and states, are always in sync with the actual DOM.
So every time something changes anywhere in a prop or state, react needs to run its render cycle.
How would react be different without immutability:
Without immutability your react implementation may not work properly.
If react were not designed for immutability, then it would not be react (i.e. a state machine) but a different beast altogether.
Immutable Data Structure with ReactJS
The first of all, react team strongly recommend applying immutable data structure like Immutability Helpers or immutable.js. Why? Because we can use "shallow comparison" to increase component re-render performance. like
MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
}
render() {
...
}
}
According to immutability, the data alway return a new reference if it has been changed. We can easy use shallowEqual(only check reference whether is same or not) to determine component will re-render. If we dont use immutable data, we have to check props or state object deeply not reference to make sure re-rendering.
As for my understanding, each component in React has its own standalone scope and they don't share the variables.
That means when you pass an mutable variable(such as Object or Array) through props to a specific react component. It will clone each variable so that this component will have a totally new environment.
For example, assuming you have component A, and it works like this,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<SubComponent user={user} />
);
}
});
What ComponentA wants is simply render the user. So it require another module, let's say SubComponent to do that.
var SubComponent = React.createClass({
render: function() {
return (
<div>
<span>Name: {this.props.user.name}</span>
<span>Role: {this.props.user.role}</span>
</div>
);
}
});
For now, we should notice the variable user in ComponentA is different with the variable this.props.user in SubComponent. The this.props.user is not a reference. It's cloned from the ComponentA.
So that means, when you try to change the value of this.props.user in SubComponent, it won't destroy the user in ComponentA. Which is what David Nolen said in his tech talk. ("Change something in data without destroy the old one.")
Of course this would sacrifice some extra spaces, but you can get lots of benefits. Such as each of your component would be totally separated. Then all the nightmares cause by Shared Mutable Variables are gone. Shared Mutable Data is the root of evil, it's unpredictable and unreliable.
Imagine the SubComponent and the ComponentA are share the same user and you want to render another module by passing props user. Then you will update your code into this way,
var ComponentA = React.createClass({
render: function() {
var user = { name: 'Tyler', role: 'Developer' };
return (
<div>
<AnotherComponent user={user} />
<SubComponent user={user} />
</div>
);
}
});
Once we change the name of user in SubComponent(maybe by accident), we will have a cascading effect, and we don't know which one change the variable. That's painful coz then we have to check each line of the code in SubComponent and AnotherComponent. You really don't want to do that, right?
So I think that's what he mean. Hope this can solve your problem. : )