App [4]
|
___________________________________
| |
Child1 [3] Child2 [5]
| |
Child1a [2] function displayProperty() [6]
|
function onSetProperty() [1]
I'm just beginning to learn React and trying to understand the built-in communication channels between components without using Redux state management. Can anyone provide a bit of guidance on a best practice when calling functions on child components?
My current approach with useRef and useImperativeMethods works but I'm not sure if this is a best practice because the docs state "As always, imperative code using refs can/should be avoided in most cases."
Is this one of those cases where a forwardRef with useImperativeMethods should be used?
Current program flow is:
Set property value in Child1a component
The onSetProperty event is bubbled up though Child1a => Child1 => App
The App component contains a variable 'const child2Ref = useRef()'
Child2 is defined as:
const Child2 = forwardRef((props, ref) => {
useImperativeMethods(ref, () => ({
displayPropertyOption(value) {
console.log(The value is ${value});
}
}
));
https://reactjs.org/docs/hooks-reference.html#useimperativemethods
You always want to have your state at the highest needed/possible component level. To modify that state or display it in a child component, you pass the state variable and the function (which modifies that state) itself to the child component which calls the
given function.
Related
I have two functional components. The only difference between them is that one is named. Like this:
const one = (props) => (<div>i am the first one</div>);
switch (type) {
case 1:
return one;
case 2:
return (props) => (<div>i am the second one</div>);
}
So the problem is the behavior is not the same. When the named component just rerenders itself after props change, the anonymous component recreates itself (call componentDidMount and componentDidUnmount). Could anyone explain what's happening there?
Named component is created once, and then you use a reference to it in a switch or somewhere else.
Unnamed function is created every time you call it.
Javascript functions are objects.
The object never equals another object except for the reference to itself.
Since the previous unnamed function doesn't equal the new one, React remounts it.
What is the best practice to define proptypes in a Parent that only passes props to a Child without using it?
Simple use case:
const Parent = ({ p0, p1 }) => (
<section className="Parent">
<div>I'm the parent</div>
<p>{p0}</p>
<Child p1={p1} />
</section>
);
Parent.propTypes = {
p0: PropTypes.string.isRequired,
// p1 ?
};
And the child component:
const Child = ({ p1 }) => (
<section className="Child">
<div>I'm a Child</div>
<span>{p1}</span>
</section>
);
Child.propTypes = {
p1: PropTypes.string.isRequired,
};
Here the options for the Parent :
Do not define p1 propTypes and use // disable-eslint-next-line react/propTypes
p1: PropTypes.any.isRequired
p1: Child.propTypes.p1 (What if Child is exported with React.memo ?)
Use the same type as Child p1: PropTypes.string.isRequired (if Child has 10 props it can be really boring to duplicate all proptypes)
Other solution
Online example:
https://codesandbox.io/s/muddy-bush-3u243
The way I understand your code, even tough p1 is passed on to the Child, it is still part of the implementation of Parent. Logically, it is also required in Parent too.
So it is correct that you have to define it in the Parent's prop types too.
But I understand that the code can be tedious, but this is more a matter of style than of technical correctness, and styles are prone to change as paradigms and tastes change all the time. So I guess there is no right or wrong answer here, only flavors.
Of course there are some concepts here that you might be able to apply. There is always a discussion on coupling or decoupling code, the latter sometimes advocating code duplication (duplicating the prop types). But then again your prop types can run out of sync if you change on component but not the other.
Personally I would load the prop types from the Child and merge the ones you want to use into the parent:
import Child from "./Child";
const Parent = ({ p0, p1 }) => {/* ... */}
const { p1 } = Child.propTypes;
Parent.propTypes = {
p0: PropTypes.string.isRequired,
p1
};
I think you can just extend the Child propTypes from the Parent propTypes
Parent.propTypes = {
...{
p0: PropTypes.string.isRequired
}, ...Child.propTypes
};
I think this should work.
P.S. I know this code does not look very good, but you can probably use a library like lodash or Object.assign to make it prettier. Maybe even create a method to extend propTypes.
I hope it helps
I would say it depends on the nature of props:
Case 1: The props are vital data/state for your application
In this case, I would say require the props in both the parent and the child.
Case 2: The props are required by the child, but they are not so important - meaning, the parent can as well make them up
In this case, I suggest you don't require the props. Set prop types and set default props.
Note: In either case, use the same prop types for the child and the parent to ensure predictability in the application. Allowing any prop types for the parent and requiring let's say strings for the child would create a mess in the application - it's always better to be sure of which type of data to expect where than encourage ambiguity.
Second Note: If you find yourself passing several props from the parent to the child ( let's say the parent expects 10 props and you pass 8 to the child ), you may need to refactor your code to get rid of the parent because she may not be that important - but of course you are the judge of the level of importance here.
Third Note: Use the disabling of react prop-types sparingly. It is a quick way to escape problems but it is also an easy way to create an application in which you don't know which types of data are flowing around.
I have a react component that represents a document with text and some footnotes. The text should be rendered like this:
This the first footnote[1], this is the second[2].
Here is another [3].
As I'm rendering my component, I want to count up every time I see a footnote so that it's incremented. The tree can be many levels deep so you can't assume that all the footnotes are direct children of the main component.
This should also be dynamic, so that adding references updates the count.
I can't think of a very 'Reacty' way of doing this. Context (as frowned upon as it is) does not seem like the right thing, and otherwise, you have no information about neighboring components.
I think I would handle it like this...
In your container or top-level component, create an array for holding footnotes. Then pass this array down as a prop to any component that may render footnotes, and also to a footnote-rendering component which must be rendered after any of the other components.
const DocComponent = () => {
const footnotes = [];
return (
<div>
<SomeContent footnotes={footnotes} />
<SomeOtherContent footnotes={footnotes} />
<EvenDifferentContent footnotes={footnotes} />
<Footnotes footnotes={footnotes} />
</div>
);
};
Note that the footnotes array must be passed down the hierarchy via props to all components that could render a reference to a footnote. Every time a component renders a footnote reference, it adds a footnote to the array like so:
const SomeContent = ({footnotes}) => {
footnotes.push('This is the footnote text.');
const footnoteIndex = footnotes.length;
return (<p>Hermansen and Shihipar, et al [{footnoteIndex}]</p>);
};
When execution arrives to the Footnotes component, the same footnotes array instance will be passed via prop to it. At that point in execution, the array will be populated with all the footnotes that need to be displayed. And you can just render them in a straightforward way:
const Footnotes = ({footnotes}) => {
const inner = footnotes.map(
(footnote, index) => (<li>[{index+1}] {footnote}</li>) );
return (<ul>{inner}</ul>);
};
This implementation is definitely coupled to the rendering order of components. So the component order in your rendering should match the visual order you would want footnotes to appear in.
Here is a jsfiddle - https://jsfiddle.net/69z2wepo/79222/
I am very new to react. Whether one way data flow in react is achieved only through flux(action and store) ?.
<div id="container"></div>
var Hello = React.createClass({
changeState: function (event) {
this.setState({
sampleState: event.target.value
});
},
getInitialState: function () {
return {
'sampleState': 'World'
};
},
render: function () {
return ( < div > Hello { this.state.sampleState}
< input onChange ={ this.changeState}> < /input>
</div >);
}
});
ReactDOM.render( < Hello / > ,
document.getElementById('container'));
I have changed the state of a component without flux.Does this fall into one way data flow ?
The idea of React's one way data flow is that data goes down the application.
A
/ \
v v
B D
| |
v v
C E
Component A can pass data to Component B, which can pass to C. Simlarly, A can pass to D which can pass to E.
Parents components can pass data to their children and so on down the component tree.
Child components can't pass data back up to their parents.
Your example doesn't have a directional data flow, because you're only working with one component. The data isn't moving when you change state — it's encapsulated within the component.
A
If you added another component to your example and passed properties down to it, you would introduce a one-way, downwards data flow.
A
|
v
B
The Flux architecture allows you to create cycles in your flow. Data goes down the tree, then out to Flux in the form of an action. This action updates a store, which in turn updates the components that are subscribed to that store.
A <- [store]
| ^
v |
B -> [action]
This keeps the data flowing in a single direction. Any component can subscribe to a Flux store, but if that component wants to make meaningful change to a component that is higher than itself in the tree (for instance if B wants to change A), it must go round in one of these cycles, rather than simply flowing back up the tree.
The standard way for a React component to include child components is to create them in the render method and set to the children property. In my use case, children may be created before the parent is rendered and passed in through the parent's properties.
Events in the child are bubbled up to the parent as expected, but changes to the parent container do not re-render children created this way. The docs indicate that there is a difference between parent and owner relationships, the latter being established only for components created in render, so my guess is this relationship is missing and important for cascading re-renders.
Here is a simple example (fiddle)
/** #jsx React.DOM */
globalState = 'initial state';
var Child = React.createClass({
render: function() {
return React.DOM.input({
value:globalState
});
}
});
var Parent = React.createClass({
handleChange: function(e) {
globalState = e.target.value;
this.forceUpdate();
},
render: function() {
return React.DOM.div({
children: [
Child(),
React.DOM.br(),
this.props.passedChild
],
onChange: this.handleChange
});
}
});
c = Child();
p = Parent({passedChild:c});
React.renderComponent(p, document.body);
In this example, both child inputs can be edited, the onChange event is caught by the parent and a forceUpdate() is called. This does cascade down to the first child which is created in the render method, but not to the second child which is created elsewhere and passed in.
How can I update the owner of a child component so it will update as desired?
My backup plan is to wire up an event listener on child components. In my application, there is quite a bit of logic around when components are created that would make it impractical to do everything in render().
This is slightly complex to explain here, but your code will now work on the master build of React (post 0.10.0).
I'm not too sure what you're trying to accomplish here, but if you change your this.props.passedChild to this.props.passedChild() and c = Child(); to c = Child;, it'll work. Call it this.props.passedChildClass or something. You can also try this.props.passedChildFn with c = function() { return Child(); }. Whatever suits your need.
Don't create an instance of a component and pass it around (it won't be a big problem anymore soon; the return value of Child() won't be an instance anymore). In general it's bad practice because this encourages mutation. Create your children on the fly as you need them and let React handle the rest.
In my application, there is quite a bit of logic around when components are created that would make it impractical to do everything in render().
Break them into helper functions! You really don't have to stuff everything into a single render.
Also, global state is a bad idea. The fact that your child updates correctly by reading from globalState is very fragile. You must have gotten a warning in your console (providing you're using the dev build) to add an onChange handler. Go read it =).