ReactJS: Is this good way to call siblings method? - javascript

I am doing this to call siblings method (state) this way:
App.js
class App extends Components {
onClick(){
/*.. calulate total..*/
this.setState({
total: window.totalValue
})
}
render() {
return (
<Main>
{/*..*/}
<Body onClick={this.onClick}/>
<Footer total={this.state.total} />
</Main>
);
}
}
class Body extends Components {
onClick(){
this.props.onClick();
}
render(){
return <Section onClick={this.onClick} />
}
}
class Section extends Components{
render(){
return (
<button onClick={props.onClick}>Calculate</button>
)
}
}
class Footer extends Components{
render(){
return props.total;
}
}
But its hard to keep passing props to every child components when there are more. Is it okay to do something like this?
Manager.js
const Components = {}
export const connect = ( obj ) =>{
Components[obj.constructor.name] = obj;
return obj;
}
export const manager = () => {
return Components;
}
And in <Footer/>'s constructor pass this to connect function to save its reference:
/*...*/
constructor(props){
super(props)
connect(this) // saving component reference to call its method later.
}
/*...*/
And in <Section/>, add onClick and in <Footer/> add calculateTotal methods like this:
/*.. Section ..*/
onClick(){
manager().Footer.calculateTotal();
}
/*.. Footer ..*/
calculateTotal(){
this.setState({
total: window.totalValue
})
}
render(){
return this.state.total;
}
Later approaches just set the state of <Footer/> instead of passing value form <Main/> and we don't have to call every onClick method (state) when deepest child's state is called.
Thanks in advance for your answer.

Related

Should functions that call a callback function be bound?

If I pass a callback function from Parent to GrandChild, should handleClick be bound in Child and GrandChild?
Parent.js
class Parent extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Clicked!');
}
render() {
return (
<Child onClick={this.handleClick} />
);
}
}
Child.js
class Child extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { onClick: callback } = this.props;
callback();
}
render() {
return (
<GrandChild onClick={this.handleClick} />
);
}
}
GrandChild.js
class GrandChild extends React {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { onClick: callback } = this.props;
callback();
}
render() {
return (
<div onClick={this.handleClick} />
);
}
}
Functions can be accessed via props without being bound when passed down to children. It's only necessary to bind to this inside the component where the function is originally defined.
You only need to do onClick={this.props.handeClick}
or if you want to pass some data, you can do it like this:
onClick={(someData) => this.props.handeClick(someData)}
EDIT: Just to clarify, you only need to bind handleClick in Parent.js. You can then just pass this function down via props and access it in the child components using this.props.
The answer is that the context this should always be the one where the logic is, so if the logic that handles the handleClick is in the class Parent so, the context is.
Other than that there are some problems in your code.
1.Your component classes must extend React.Component or React.PureComponent and not React itself (maybe it's a copy-paste error, but if not fix it).
See: https://reactjs.org/docs/components-and-props.html#function-and-class-components
2.You don't have to name every single props that should be passed through all child components, you can use the spread syntax if you code using ES6.
See: https://reactjs.org/docs/jsx-in-depth.html#spread-attributes
class Child extends React.Component {
render() {
return (
// this is passing all props of Child to GrandChild
<GrandChild {...this.props} />
);
}
}
3.For components that don't have state, use function instead of class, it's more performant and also the code is smaller.
function Child(props) {
return (
<GrandChild {...props} />
);
}
Finally your code could look like this:
function Parent(props) {
function handleClick() {
console.log('clicked');
}
return <Child onClick={handleClick} />;
}
function Child(props) {
return <GrandChild {...props} />;
}
function GrandChild(props) {
return <div onClick={props.onClick} />;
}
Arrow function is better. And context this will be automatically bind.
handleClick = () => {}
Inline function is bad (unnecessary render possible). It is better like this:
handleClick = (someData) => this.props.handeClick(someData)
And
onClick={this.handleClick}

call child function from parent in react 16

After upgrading to react 16 I am getting null in console.log(this.child)
My parent component
import EditReview from './partials/editReview'
class VenueDetails extends Component {
constructor(props) {
super(props)
this.child = React.createRef();
}
editButtonClick = () => {
console.log(this.child)
this.child.current.onEditClick()
}
render() {
return (
<div>
<button className="pull-right" onClick={() => this.editButtonClick(review, i)}>edit</button>
<div className="place-review-text">
<EditReview {...this.props}/>
</div>
</div>
)
}
}
My child component
class EditReview extends Component {
onEditClick(review, editIndex) {
console.log('ppp')
}
render() {
return ()
}
}
export default EditReview
I need to call onEditClick from the parent component. I tried this but doesn't work.
Kindly help me
You have to assign the ref:
<EditReview {...this.props} ref={this.child} />
Also, you don't need to use inline arrow function:
onClick={() => this.editButtonClick(review, i)}
// ------^^^^^ not required
// because, you're already using public class method
Just use:
onClick={this.editButtonClick(review, i)}
Define your method like this:
editButtonClick = (review, index) => { // to access review, i

How can I wrap the Children of a Parent component in a HOC and render them in React?

Let me start by saying that this example is very simple and can be solved with React.cloneElement. But I want more freedom and the project will be more complex, so I'd like to find a solution.
I would also like to understand what I'm missing :/
I want to be able to augment the children of a Parent component with props and methods (hence the HOC). It would start from here:
<Parent>
<aChild />
<anotherChild />
<yetAnotherChild />
</Parent>
And this is the Parent component (called Sequence in my project), so far:
import React, { Component } from 'react';
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends Component {
constructor(props) {
super(props);
this.state = {
pointer: 0,
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Children = React.Children.map(this.props.children, Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
// do stuff
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
export default Sequence;
I get the following error:
You can play with the code here: https://codesandbox.io/s/6w1n5wor9w
Thank you for any help!
This answer will not solve your problem but maybe gives a hint why this is not possible. At first I was surprised why your code does not work, even though I'm not an experienced React developer it seems ok map this.props.children through with React.Children.map and return the desired Component with your HOC. But it did not work.
I tried to debug it a little bit and did some search. I've learned props.children actually contains the elements itself not the instances of components. Even, React.Children.map does not have any effect on this.
Here is a working snippet proves that your problem is not related with the HOC. I've used an array of components instead of mapping through props.children.
class Line1 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 1</div>;
}
}
class Line2 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 2</div>;
}
}
class Line3 extends React.Component {
componentDidMount() {
setTimeout(this.props.onAnimationEnd, 1000);
}
render() {
return <div>Line 3</div>;
}
}
const withNotification = handler => Component => props => (
<Component onAnimationEnd={handler} {...props} />
);
class Sequence extends React.Component {
constructor(props) {
super(props);
this.state = {
pointer: 0
};
this.notifyAnimationEnd = this.notifyAnimationEnd.bind(this);
this.Arr = [ Line1, Line2, Line3 ];
this.Children = this.Arr.map(Child =>
withNotification(this.notifyAnimationEnd)(Child)
);
}
notifyAnimationEnd() {
this.next();
}
next() {
// Clearly, render the next element only if there is, a next element
if (this.state.pointer >= this.Arr.length - 1) {
return;
}
this.setState({ pointer: this.state.pointer + 1 });
}
render() {
return (
<div>
{this.Children.map((Child, i) => {
if (i <= this.state.pointer) return <Child />;
return <div>nope</div>;
})}
</div>
);
}
}
ReactDOM.render(
<Sequence />,
document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
You are returning <Child /> instead of Child in Sequence.js render method. Here is my edited copy - codesandbox

React - Instance creation and usage of a class

I'm trying to get an instance of the class ActionEditor So that I'd be able to use its methods later:
function render() {
const toRender = responseActions.map((actionInstance) => {
currentActionEditing=actionInstance;
return <li>{ actionInstance === expandedAction ? <ActionEditor id={actionInstance.title} action={getActionByKey(actionInstance.actionType)} instance={actionInstance} state={actionInstance} /> : <button onClick={createOnClick(actionInstance)}>{actionInstance.title}</button>}</li>;
});
ReactDOM.render(
<div>
<div>{toRender}</div>
<button style={styleButtonGenerate} onClick={onGenerateClick}>Generate</button>
</div>,
document.getElementById('root')
);
}
I've attempted to use it through an onClick method like so:
function onGenerateClick() {
var editor = document.getElementById(currentActionEditing.title);
editor.prototype = ActionEditor;
editor.methodIWantToUse();
}
But it always turns out to be null/undefined.
I understand that it's not the best example but it should be enough to demonstrate the issue.
Is there a way around this?
I think what you want here is to save a ref to the component so it can be accessed, see in the example below how the sayHi method is called from the parent component.
class MyComponent extends React.Component {
sayHi() {
console.log('hi');
}
render() {
return (<div>I'm a component!</div>)
}
}
class App extends React.Component {
render() {
// just a way to show how to access a child component method.
setTimeout(() => {
this.node.sayHi();
}, 1000)
return (<MyComponent ref={(node) => this.node = node}/>)
}
}
ReactDOM.render(<App />, document.querySelector("body"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

React fetching data then passing as props?

I have a view that contains several components.
class ProfileView extends React.Compnent {
constructor() {
super();
this.state = {
user: {}
}
}
componentWillMount() {
fetchData((res) => {
this.setState({user: res});
})
}
render() {
return (
<div>
<SomeComponent user={this.state.user} />
<AnotherComponent />
</div>
)
}
}
Since this is making an async call and rendering the state as an empty object on the initial redner, it's causing what I think to be a problem?
Inside my inner component I have to write validation, which is ok but feels wrong, that is why I am asking this question, is the validation in the example below good practice or am I making a mistake.
class SomeComponent extends React.Compnent {
render() {
if(typeof this.props.user !== 'undefined' && !$.isEmptyObject(this.props.user)) {
return (
<div>
<SomeComponent user={this.state.user} />
<AnotherComponent />
</div>
)
} else {
return (
<div></div>
)
}
}
}
This is the best I could come up with, it works ok but there is a slight jump in my UI because initially I am just rendering a <div>.
How can I improve my approach, or is this the ideal way to do it?
Your implementation is close to what I would do. I think that the best solution is to initially render a component that indicates to the user that data is being fetched from the server. Once that data comes back, you can update the state of your parent component, which will case the child component to render instead. A potential solution might look something like this:
function renderChildComponent() {
const {user} = this.state;
if (user) {
return <Child user={user} />;
}
return <Loading />;
}
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
user: undefined
};
}
componentDidMount() {
fetchData(response => {
this.setState({user: response});
});
}
render() {
return (
<div>
{renderChildComponent.call(this)}
</div>
);
}
}

Categories

Resources