How to add Error display to React components - javascript

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 />
}
}
}

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" />

Delay in Rendering a Component?

I'm attempting to make my own personal website, and trying to use React to do so. In the process, I intend to make each section a different React Component. My plan is to have the navbar at the top be able to select which component is currently "active", and actually gets rendered and shown. In addition, when switching to a new section, I would like the old component to have a "leaving" animation, and the new component to have an "entering" animation (these are done with react-motion). However, currently both the entering and leaving are done at the same time, because I'm changing the active state for both components at the same time. Is there any way to delay one component becomes active after another one becoming inactive?
The parent component that houses each section looks like so:
class Website extends React.Component {
constructor(props){
super(props)
this.state = {
homeActive: true,
aboutActive: false
}
homeActivator(){
this.setState({
homeActive: true,
aboutActive: false
})
}
aboutActivator(){
this.setState({
homeActive: false,
aboutActive: true
})
}
render(){
return (
<div>
<NavBar handleHome={this.homeActivator.bind(this)} handleAbout=
{this.aboutActivator.bind(this)}/>
<Home active={this.state.homeActive} />
<About active={this.state.aboutActive} />
</div>
}
And then one of the "sections" would look like so:
class Home extends React.Component{
render() {
let content = (
<div>
Home
</div>
)
if (!this.props.active){
return (
//Some jsx that results in the content leaving the page
)
}
return(
//Some jsx that results in the content entering the page
)
}
}
I did not have a ton of time to answer this, but came up with the best example I could. It's not an exact replica of what you are looking to do, but is very similar, so if you understand it, you will be able to figure out your problem quite easily.
To make things a little easier to understand, I am mimicking components with methods placed inside the React Class. Obviously in the real world, you would be importing your components from other files. I'm sure you'll understand what's going on.
export default class Example extends Component {
constructor(props) {
super(props)
this.state = {
c1: true,
c2: false
}
}
// Component One
renderc1() {
return (
<div>
I am component one
</div>
)
}
// Component Two
renderc2() {
return (
<div>
I am component two
</div>
)
}
changeComponents = () => {
this.setState({ c1: false })
setTimeout(() => {
this.setState({ c2: true })
}, 1500)
}
render() {
return (
<div className="example">
{this.state.c1 ? this.renderc1() : null}
{this.state.c2 ? this.renderc2() : null}
<button onClick={this.changeComponents}>Click me</button>
</div>
)
}
}
Clicking the button will fire off the changeComponents function, which will then immediately set the state of "c1" to false. A setTimeout after that ensures that component 2 will be delayed rendering to the screen.
Notice the arrow syntax, I used, which binds the this keyword to the class, so you don't have to worry about writing bind this everywhere.

React components only setting state for one key in constructor function. ES6

I have this component, but it's not setting show in the the state constructor. I can console.log the props and they show the correct params, but for some reason, show is not getting set.
class SubstitutionPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
suggestions: this.props.synonyms_with_levels,
show: this.props.show
}
}
handleToggleShow() {
this.setState({
show: false
})
}
render() {
console.log("sub panel")
console.log(this.state)
console.log(this.props)
if (this.props.synonyms_with_levels.length > 0 && this.state.show) {
return(
<div className="substitution-panel">
<div onClick={() => this.handleToggleShow()} className="glyphicon glyphicon-remove hover-hand"></div>
{this.props.synonyms_with_levels}
</div>
);
} else {
return (
<span>
</span>
);
}
}
}
The parent that renders this child component looks like this:
<SubstitutionPanel synonyms_with_levels= {this.props.synonyms_with_levels} show={this.state.showSubPane} />
I'm really just trying to make a "tooltip" where the parent can open the tooltip.
Is everything ok when you console.log(this.props)?
It may be just a typo here, but in the parent component you have
show={this.state.showSubPane}
and maybe it should be 'showSubPanel' with an L at the end?

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...

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