React fetching data then passing as props? - javascript

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>
);
}
}

Related

unable to find a React.Component by id

I have a React.Component with render() declared this way:
render(){
return <div>
<button id="butt" onClick={()=> $("#noti").change("test") }>click me</button>
<Notification id="noti" onMounted={() => console.log("test")}/>
</div>
}
And this is my Notification class:
class Notification extends React.Component {
constructor(props) {
super(props)
this.state = {
message: "place holder",
visible: false
}
}
show(message, duration){
console.log("show")
this.setState({visible: true, message})
setTimeout(() => {
this.setState({visible: false})
}, duration)
}
change(message){
this.setState({message})
}
render() {
const {visible, message} = this.state
return <div>
{visible ? message : ""}
</div>
}
}
As the class name suggests, I am trying to create a simple notification with message. And I want to simply display the notification by calling noti.show(message, duration).
However, when I try to find noti by doing window.noti, $("#noti") and document.findElementById("noti"), they all give me undefined, while noti is displayed properly. And I can find the butt using the code to find noti.
How should I find the noti? I am new to front end so please be a little bit more specific on explaining.
It's not a good idea using JQuery library with Reactjs. instead you can find a appropriate react library for notification or anything else.
Also In React we use ref to to access DOM nodes.
Something like this:
constructor(props) {
super(props);
this.noti = React.createRef();
}
...
<Notification ref={this.noti} onMounted={() => console.log("test")}/>
more info: https://reactjs.org/docs/refs-and-the-dom.html
I have hardcoded the id to 'noti' in the render method. You can also use the prop id in the Notification component.I have remodelled the component so that you can achieve the intended functionality through React way.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
messageContent: 'placeholder'
}
}
setMessage = (data) => {
this.setState({messageContent : data});
}
render() {
return (
<div className="App">
<button id='butt' onClick= {() => this.setMessage('test')} />
<Notification message = {this.state.messageContent} />
</div>
);
}
}
class Notification extends React.Component {
render () {
const {message} = this.props;
return (
<div id='noti'>
{message}
</div>
)
}
}
Before beginning: Using id/class to reach DOM nodes is not suggested in React.js, you need to use Ref's. Read more at here.
In your first render method, you give id property to Notification component.
In react.js,
if you pass a property to some component, it becomes a props of that
component. (read more here)
After you give the id to Notification, you need to take and use that specific props in your Notification component.
You see that you inserted a code line super(props) in constructor of Notification? That means, take all the props from super (upper) class and inherit them in this class.
Since id is HTML tag, you can use it like:
class Notification extends React.Component {
constructor(props) {
// inherit all props from upper class
super(props);
this.state = {
message: "place holder",
visible: false,
// you can reach all props with using this.props
// we took id props and assign it to some property in component state
id: this.props.id
}
}
show(message, duration){
// code..
}
change(message){
// code..
}
render() {
const {visible, message, id} = this.state
// give that id to div tag
return <div id={id}>
{message}
</div>
}
}
You can't pass id/class to a React Component as you would declare them in your normal HTML. any property when passed to a React Component becomes a props of that component which you have to use in the component class/function.
render() {
const {visible, message} = this.state
// give your id props to div tag as id attr
return <div id={this.props.id}>
{message}
</div>
}
This answer does not provide the exact answer about selecting a component as you want. I'm providing this answer so you can see other alternatives (more React way maybe) and improve it according to your needs.
class App extends React.Component {
state = {
isNotiVisible: false
};
handleClick = () => this.setState({ isNotiVisible: true });
render() {
return (
<div>
<button onClick={this.handleClick}>Show Noti</button>
{this.state.isNotiVisible && (
<Noti duration={2000} message="This is a simple notification." />
)}
</div>
);
}
}
class Noti extends React.Component {
state = {
visible: true
};
componentDidMount() {
setTimeout(() => this.setState({ visible: false }), this.props.duration);
}
render() {
return this.state.visible && <div>{this.props.message}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

more efficiency way for performance in React Component Mounting

I've made many Modals in React.
I found 2 ways of making Modal.
The first one is like this
class Modal extends React.Component {
componentDidMount(){ console.log('DidMount') };
componentDidUpdate(){ console.log('DidUpdate') };
componentWillUnmount(){ console.log('WillUnmount') };
render(){
return (
<React.Fragment>
<div className="overlay" />
<div className="Modal>
This is Modal.
</div>
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
isModalOpen: false
}
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen })
render(){
return (
<div className="App">
<button onClick={this.toggleModal}>Toggle</button>
{ this.state.isModalOpen ? <Modal /> : null }
</div>
)
}
}
This one repeats componentDidMount()&componentWillUnmount() when the state changed.
Let's see the other one.
class Modal extends React.Component {
componentDidMount(){ console.log('DidMount') };
componentDidUpdate(){ console.log('DidUpdate') };
componentWillUnmount(){ console.log('WillUnmount') };
render(){
return (
<React.Fragment>
{ props.isOpen ? <div className="overlay" /> : null }
{ props.isOpen ? <div className="Modal">This is Modal</div> : null }
</React.Fragment>
)
}
}
class App extends React.Component {
state = {
isModalOpen: false
}
toggleModal = () => this.setState({ isModalOpen: !this.state.isModalOpen })
render(){
return (
<div className="App">
<button onClick={this.toggleModal}>Toggle</button>
<Modal isOpen={this.state.isModalOpen} />
</div>
)
}
}
This one would not call componentWillUnmount().
It would call componentDidUpdate() when the state changed.
I wonder which one is a better way for the performance or something else.
Thank you in advance.
React.Fragment is a little bit fast and uses less memory because it doesn't need to create an extra DOM node.
With that being said unless your application is big and complex I wouldn't worry about it. I'm not exactly sure what wrapping the modal div in a React.Fragment is achieving.
You question is a bit confusing but I will attempt to clarify a few things.
Regarding your comment: This one would not call componentWillUnmount(). It will not call the the cWU() method because you are always rendering it by using this <Modal isOpen={this.state.isModalOpen} /> within your render. Now wether it appears or not based on your isOpen prop it's a different issue. On the other hand if you had something like {isThisPropertyTrue ? <Modal isOpen={this.state.isModalOpen} /> : null}, and your isThisPropertyTrue was toggling from true to false, then you would notice the console.log in your unmount hook.
This method { this.state.isModalOpen ? <Modal /> : null } has a better performance since the modal is render upon request.

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

How to add Error display to React components

I have many working components im my React application.
I want add the functionality of displaying a simple error/warning message (for the sake of the simplicity, just a H1 elem on the top) when the component (that can be a whole window/form) find some problem.
For example, in a form that change a password, It would be nice to have something like:
onSubmit(event) {
...
if (!this.state.password1 === this.state.password2) {
this.showError("The passwords donĀ“t match"}
...
}
My first intent is was using inheritance, defining a super class this way:
export default class MostrarError extends React.Component {
constructor(props) {
super(props);
this.state = { error: '' };
}
mostrarError(msg) {
this.setState({ error: msg });
}
render() {
return (
<React.Fragment>
{this.state.error ? <h1> UPS! {this.state.error} </h1> : null}
this.props.children
</React.Fragment>
);
}
}
but, the super render() method will be never be called, because each subclass defines its own render().
I can use some kind of Method Template, but it involves modifying all subclasses.
This is surelly a very common problem, but most solutions I found makes use of Redux, and I cant use it.
Can someone point me to some other solution?
I think what you're looking for is to have one single alert component that can communicate with parent component and conditionally render itself.
Why not do something like this in your parent component
export default class ParentCompo extends React.Component{
state = {
error: false,
errorMsg: ''
}
onSubmit = () => {
if(this.state.password1 !== this.state.password2) {
this.setState({error: true, errorMsg: 'Passwords dont match'})
}
}
render(){
return (
<div>
<AlertCompo error={this.state.error} errorMsg={this.state.errorMsg} />
Normal stuff here...
</div>
)
}
}
Your alert component should look something like this
export default class AlertCompo extends React.PureComponent{
shouldComponentUpdate(nextProps){
if(nextProps.error !== this.props.error){
return true;
}
return false;
}
render(){
if(this.props.error){
return (
<h1>Some alert message {this.props.errorMsg}</h1>
)
}else {
return <React.Fragment />
}
}
}

What's the proper way to pass dependencies between components in React?

Imagine that Component A creates a list of items that Component B needs to display. What's the proper way to pass data from Component A to Component B from their parent?
For example, let's say that Component A's constructor creates a list of items and has a function _getListItems() that returns that list. I'm hoping the parent can then pass that list on to other components via props.
My naive (non-working) implementation has their parent attempting to render the components like this:
render () {
return (
<div>
<h1>Data Test</h1>
<ComponentA ref='compa'/>
<ComponentB items={this.refs.compa._getListItems()}/>
</div>
);
}
....although the code above doesn't work, I hope it illustrates what I'm trying to do.
ps. nOOb to react and javascript, so forgive me if the answer to my question's obvious...
Divide your components into two separate categories.
Presentational Component that has responsibility to display a thing. This component should not have state (except for UI state).
Container Component that knows the data.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.skmxo7vt4
So, in your case the data should created by parent of ComponentA and ComponentB and pass the data to both ComponentA and ComponentB via props.
Example:
render(){
let items = this._getListItems();
return (
<div>
<ComponentA items={items} />
<ComponentB items={items} />
</div>
);
}
Edit
Rewrite OP's approach in the comment:
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.state = { stuff: [1,2,3] };
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} />
<ComponentB items={this.state.stuff} />
</div>
);
}
}
Following the accepted answer above, I've just had a (related) EUREKA moment, so I'm going to expand on the answer; when the parent uses its own state to pass props to its children, whenever the parent's state changes, its render() function is called, thus updating the children with the updated state. So you can do stuff like this:
class MyContainer extends React.Component {
constructor(props) {
super(props);
let sltd = this.props.selected
this.state = {
stuff: [1,2,3],
selected: sltd
};
}
_handleAStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
_handleBStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleAStuff.bind(this)} />
<ComponentB items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleBStuff.bind(this)} />
</div>
);
}
}
MyContainer.defaultProps = {
selected: 0
}
class ComponentA extends React.Component {
constructor(props) {
super(props)
}
_handleSelect(value) {
this.props.parentFunc(value.label)
}
render() {
const itm = this.props.items.map(function(values) {
return { value: values, label: values}
})
return (
<div>
<Select
options={itm}
value={this.props.selected}
onChange={this._handleSelect.bind(this)}
/>
</div>
);
}
}
// ComponentB...
The callback pattern above means that ComponentA and ComponentB do not need to maintain state, they simply 'render stuff', which is also pretty cool. I'm beginning to see the power of REACT...

Categories

Resources