I've used React for a couple of weeks now but I have this simple problem that I can't seem to wrap my head around. It's about creating new html elements.
I would just like to know in general if the way that I went about it, is the "right way" or is there another preferred way to create new html element with a click function.
For some reason this problem took awhile for me to figure out and it still feels a bit strange, that's why I'm asking.
Thanks in advance!
import React, { Component } from 'react';
import './Overview.css';
import Project from './Project';
class Overview extends Component {
constructor() {
super()
this.state = {
itemArray: []
}
}
createProject() {
const item = this.state.itemArray;
item.push(
<div>
<h2>Title</h2>
<p>text</p>
</div>
)
this.setState({itemArray: item})
//console.log(this.state)
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return <div className="box" key={index}>{item}</div>
})}
</div>
</div>
);
}
}
export default Overview;
No, this is not a correct approach. You shouldn't be generating HTML elements like that, nor keep them in state - it is against React to manipulate DOM like that. You won't be able to utilize Virtual DOM is the first thing that I can think of.
What you should do instead is keep all data that is needed for rendering in state and then generate the HTML element from there, for instance
createProject() {
const item = this.state.itemArray;
const title = '';
const text = '';
item.push({ title, text })
this.setState({itemArray: item})
}
render() {
return (
<div className="Overview">
<p>Overview</p>
<button onClick={this.createProject.bind(this)}>New Project</button>
<Project />
<div>
{this.state.itemArray.map((item, index) => {
return (
<div className="box" key={index}>
<div>
<h2>{item.title}</h2>
<p>{item.text}</p>
</div>
</div>
)
})}
</div>
</div>
);
}
Related
I am trying to use Typed.js on my website, and I have been getting many errors I cant find a way to fix them.
I have looked through GitHub, and I have found the exact same problem that someone had I used that code in my document and the errors are still popping up. The errors are always Expression Expected and } expected also, Unexpected token. Did you mean {'}? Here is a picture of the errors too. Here is the errors that are resulting:
errors
import "./App.css";
import Typed from "typed.js";
import { useEffect, useRef } from "react";
function HomePage() {
return (
<>
<body>
<div class="hero">
<nav>
<h2 class="logo">JAKEISTHATMAN.</h2>
<ul>
<li>
Home
</li>
<li>
About
</li>
<li>
Services
</li>
<li>
Portfolio
</li>
</ul>
<button type="button">Contact</button>
</nav>
</div>
<div class="container"></div>
<div class="main-text">
<h1>
I'm a <span class="type"></span>
</h1>
<p>
Also I work for <b>#no</b> media team!
</p>
<button type="button">Contact</button>
</div>
export default function Typed() {
const TypedElement = useRef(null);
useEffect(() => {
if (!TypedElement.current) return;
const typed = new Typed(TypedElement.current, {
strings: [
"string",
"string2",
"string3",
],
startDelay: 300,
typeSpeed: 100,
backSpeed: 100,
backDelay: 500,
loop: true,
});
// Destroying
return () => {
typed.destroy();
};
}, []);
return <span ref={TypedElement}></span>
}
</body>
</>
);
}
export default HomePage;
First, if the code example above is how your code really is, it won't work because you just have javascript in an html tag all of a sudden. Try wrapping the javascript with curly brackets to let the file know you are typing javascript code now like this:
<div>
{console.log("this is javascript")}
</div>
Second, it looks like you are trying to define a functional component called Typed. At the very least, it is poor organization to define a functional component inside an html tag like you have done above (if it isn't impossible, not sure, never tried it). You should define the functional component called Typed outside the HomePage function, like so:
import "./App.css";
import Typed from "typed.js";
import { useEffect, useRef } from "react";
const Example = ({args_if_necessary}) => {
const typeTarget = useRef(null);
useEffect(() => {
const typed = new Typed(typeTarget.current, {
strings: ["<i>First</i> sentence.", `use ${args_if_necessary}`],
typeSpeed: 40,
});
return () => {
typed.destroy();
};
}, []);
return <span ref={typeTarget} />;
};
function HomePage() {
return (
<>
<body>
<your-other-html />
<Example args_if_necessary={"something"} />
</body>
</>
);
}
export default HomePage;
I have a web app that uses a websocket to receive information from an API I have put together.
Everything works great, however, every time new information arrives from the websocket, the whole list on my frontend (React) is updated.
Here is the relevent code:
componentDidMount(prevState) {
socketIO.on('newNotification', (response) => {
const notifications = this.state.notifications;
console.log(response)
const newNotifications = response.data
this.setState(prevState => ({
notifications: [...this.state.notifications, newNotifications]
}))
});
}
notifications is a list of notifications that is received from my API, which I set to the state.notifications whenever a response is received.
My understanding is React only updates what it needs to, so I'm not sure what is going on.
Here is my Notification component:
import React from "react"
class Notification extends React.Component {
render(){
return(
<ul>
<li
key = {this.props.notification.id}
onClick={() => this.props.deleteNotificationProps(this.props.notification.id)}>
<div className='separator-container'>
<div className={'notification-border ' + this.props.notification.stat_abr}>
<div className='notification' >
<div className='left-notification'>
<div className = 'stat-abr'>{this.props.notification.stat_abr}</div>
<div className = 'game-time'>{this.props.notification.game_time_string}</div>
</div>
<div className='middle-notification'>
<div className='player-image'>
<img src={"http://nhl.bamcontent.com/images/headshots/current/168x168/" + this.props.notification.player_id.toString() + ".jpg"} alt="" className="player-img" />
</div>
</div>
<div className = 'right-notification'> {this.props.notification.description} </div>
</div>
</div>
</div>
</li>
</ul>
)
}
}
export default Notification
I tried various diferent methods of updating the state, but nothing seems to work.
EDIT: here is the NotificationList class where the Notification component is created:
class NotificationList extends React.Component {
render() {
return(
<ul>
{this.props.notifications.map(notification => (
<Notification
id = {notification.id}
notification = {notification}
handleChangeProps = {this.props.handleChangeProps}
deleteNotificationProps = {this.props.deleteNotificationProps}
/>
))}
</ul>
)
}
}
I can't see the code where you iterate over the notifications to create Notification components.
I assume you have a notifications.map(...) somewhere... To only re-render new components, use the key={...} attribute inside of the map, with a value unique to each attribute (use index if you don't have a unique key).
e.g.
<div>
{ notifications.map((notification) => <Notification
key={notification.id}
notification={notification}
/>)
}
</div>
Figured out what was wrong:
I had a <ul></ul> tag surrounding my <li></li> tag in my Notification class.
Removed this and all is working as it should.
I am trying to toggle react state after the button click. After clicking button Work From Office should change to work From Home and vice versa. But it is not working. What am I dong wrong? I am able to change only once. Can we do with if statement? What is simple way?
** React **
import React, { Component } from 'react';
import './ChangeSchedule.css';
class ChangeSchedule extends Component {
constructor(){
super()
this.state = {
// work:'from office'
workFromOffice:true
}
}
changeMyWorkPlace(){
this.setState({
// work:'from Home'
workFromOffice:!this.state.workFromOffice
})
}
render(){
return(
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
{/* <button class="chageScheduleBtn " onClick = {()=> this.changeMyWorkPlace()}> Change My Schedule </button> */}
<button class="chageScheduleBtn " onClick = {()=> this.workFromOffice() ?'Home': 'Office'}> Change My Schedule </button>
</div>
</div>
)
}
}
export default ChangeSchedule;
You can use a ternary expression to display content for each state.
For example:
{this.state.workFromOffice ? " from Office" : " from Home"}
Now this button should work as you expect:
<button class="chageScheduleBtn" onClick={()=> this.changeMyWorkPlace()}>
Change My Schedule
</button>
See codesandbox for fully working example
You could do it as below. Just change the status when the click happen. And inside the button, use a ternary expression. Like so:
import { Component } from "react";
import "./ChangeSchedule.css";
class ChangeSchedule extends Component {
constructor() {
super();
this.state = {
// work:'from office'
workFromOffice: true,
};
}
changeMyWorkPlace() {
this.setState({
// work:'from Home'
workFromOffice: !this.state.workFromOffice,
});
}
render() {
return (
<div>
<div class="schedule change">
<h3>Emplyoee Name: </h3>
<p>Today Pooja is work {this.state.work}</p>
<button class="chageScheduleBtn " onClick={() => this.workFromOffice()}>
{this.state.workFromOffice ? "Work From Home" : "Work From Office"}
</button>
</div>
</div>
);
}
}
export default ChangeSchedule;
The answer is in the way you're structuring your state. You can make it really simple by just using one entry of the state - workFromOffice. Then, your click handler should care only about changing that state value to the opposite of what was set before. Example:
onClick={() => this.setState({ workFromOffice: !this.state.workFromOffice })}
When the changeMyWorkPlace function created, it captures your initial state and uses it everytime you run the function so only works once. You should instruct react to use up to date state.
try this way.
changeMyWorkPlace(){
this.setState((previousState) => ({
// work:'from Home'
workFromOffice:!previousState.workFromOffice
}))
}
I'm trying to add a custom HTML attribute on a React component:
const Parent = () => (
<div className="parent">
<Child data-custom="foo" />
</div>
);
Where Child is another React component, but the attribute gets stripped on the output HTML. I'm aware that I could simply add the attribute on the Child root element, like so:
const Child = () => <div className="child" data-custom="foo" />
Or read the attribute in the Child component via props, but that's not what i'm looking since that would couple the attribute with the component, and I'm looking for a more contextual approach (the attribute would be used for test automation purposes).
Is there a clean way to do that in React?
I'm also considering writing a Babel plugin to add the attribute or prevent the stripping, but that would be another question.
Thanks!
React element doesn't necessarily correspond to DOM element that could have HTML attributes. React component can be rendered to multiple DOM elements or no elements at all.
If Child should be able to provide additional props or attributes to child DOM element, it should pass them explicitly:
const Child = props => <div className="child" {...props} />
This way data-custom="foo" will be passed to <div>.
For this, you can try this in your react script.
const MyCompo = () => {
return (
<div>
<p>HTML Code</p>
</div>
);
}
export default About;
Otherwise you can create class and then define your components and then export them.
import React from 'react';
import '../assets/css/style.css';
import '../assets/css/pure-min.css';
import '../assets/css/custom.css';
import 'font-awesome/css/font-awesome.min.css';
import $ from 'jquery';
class FirstScreen extends React.Component {
constructor(props) {
super(props);
this.handleLoad = this.handleLoad.bind(this);
}
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
handleLoad() {
}
render() {
return <div>HTML Code</div>;
}
}
export default FirstScreen;
You could use the below syntax
const Parent = () => (
<div className="parent">
<Child data-custom="foo"/>
</div>
);
const Child = ({...props}) => (<div className="child" {...props} />)
For example you could have a directive in angular like so:
angular.module('app')
.directive('classy', function() {
return {
restrict: 'A',
link: function($scope, $el) {
$el.addClass('stay-classy');
}
}
}
And implement like so:
<div classy></div>
There doesn't seem to be an equivalent in React that I've seen after reading through most the docs and googling. I was hoping for something like:
...
render: function() {
return (
<MyComponent classy></MyComponent>
);
}
Is there something like that possible that I've been missing? Is there a different yet functionally similar equivalent? Or maybe this question just shows that I'm missing some part of the "React way" and I shouldn't ever want to do this. Thanks!
It will be helpful to consider what Angular and React are each doing "behind the scenes."
In your Angular example, when you write <div classy/></div> you're saying "render a DIV element and then attach to it the behaviors defined by the classy directive.
In your React example, when you write <MyComponent classy></MyComponent>, you're saying, "create an instance of MyComponent and pass it the props { classy: true }. The transpiler (Babel or whathaveyou) will turn it into the following JavaScript:
React.createElement(MyComponent, { classy: true });
So the answer to your question is that you can't write <MyComponent classy></MyComponent> because MyComponent component doesn't know what to do with the classy prop. In React, you might write something like this instead:
class ClassyDiv extends React.Component {
render() {
const { className, ...rest } = this.props;
return <div className={`${className || ''} stay-classy`} {...rest}/>;
}
}
This works because we know the React.DOM.div component (like most DOM components) knows what to do with the className prop.
Since React 0.14 we can express something like this more simply, as a "pure" stateless functional component, i.e. a function that accepts props and returns the rendered result:
function AlsoClassyDiv(props) {
const { className, ...rest } = props;
return <div className={`${className || ''} stay-classy`} {...rest}/>;
};
You can see both approaches in action in the below snippet.
class ClassyDiv extends React.Component {
render() {
const { className, ...rest } = this.props;
return <div className={`${className || ''} stay-classy`} {...rest}/>;
}
}
function AlsoClassyDiv({ className, ...props }) {
return <div className={`${className || ''} stay-classy`} {...props}/>;
};
ReactDOM.render(
<div id="container">
<div>Regular div</div>
<ClassyDiv>ClassyDiv!</ClassyDiv>
<AlsoClassyDiv>AlsoClassyDiv!</AlsoClassyDiv>
</div>,
document.body
);
.stay-classy { font: bold 3em Helvetica; text-shadow: 4px 4px 2px #aaa; }
<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>
One way you could implement similar behavior is using React class mixins
A great example of a useful directive in angular is
Target
The smoothScroll directive would intercept the click event then use window scroll or jquery scrollTo to apply all manner of animation.
Anywhere in the html one could then simply use the directive powered class name.
This sort of thing is not available in React. To do it in React you would have to create a special link component to use instead of:
<a> like ASmooth....
I was looking to find a way to reproduce the directive system for applying style or play with the component.
You can create a component that play with children and then render them :
function TextCenter(props) {
// Iterates over children and clone it with custom props
const children = React.Children.map(
props.children,
(child) => React.cloneElement(child, { className: 'text-center' }
)
// Render the children
return <>{children}</>;
}
function MyComponent() {
return (
<TextCenter>
<div>
<h1>Hello centered world</h1>
<p>Yessss</p>
</div>
</TextCenter>
)
}
Here is a more powerfull example for responsive text alignement :
interface Props extends Breakpoints<'start' | 'center' | 'end'>{}
export const TextAlign: FunctionComponent<Props> = (props) => {
const className = generateClassName('text', props);
const children = React.Children.map(props.children, child => React.cloneElement(child as ReactElement, { className }))
return (
<>
{children}
</>
)
}
export const MyComponent: FunctionComponent<Props> = (props) => {
return (
<div>
<TextCenter xs="center" md="start">
<h1>I am centered on mobile but not on desktop</h1>
</TextCenter>
</div>
)
}
There are two problems with this solution, when the children is a component, it must also have the prop className and it also makes the HTML less clean as it adds a level in hierarchy.
Look my friend i didn't get you well but long story short, angularJS directives is actually a component. So the idea behind angularJs directive is to create component that has its own scope data and it's own method to operate on it. I was thinking the same way you did and found your post here and i couldn't find an answer for that. But thanks for working experience, i thought about it and know how to do it.
I wanted to add an edit button for each link item in a list to toggle the edit form for each one only so each ListItem should be a stand alone component, that way i have standalone state for each one and i toggle it on & off.