I´m creating a menu that changes his styles when I scroll, but...
this is part of my header.js:(it has to be a class..)
...
render(){
return(
<header className={`${this.classHeader}`}>
<nav>
<div>
<ul>{this.navigationItems.items.map((item,i)=><NavItem key={i} data={item} />)}</ul>
</div>
</nav>
</header>
)}}
and this is mi AppHolder.js:
class AppHolder extends Component {
constructor(props){
super(props);
this.state={
one:'navBar'
}
}
componentDidMount(){
window.onscroll = () => this.handleAnimation()
}
handleAnimation = () => {
if(document.documentElement.scrollTop > 100){
this.setState({
one:'scroll'
})
}
if(document.documentElement.scrollTop === 0 ){
this.setState({
one:'navBar'
})
}
}
render() {
return (
<div className="app">
<Header classHeader={this.state.one}/>
<MainContent />
</div>
);
}
}
but..in the HTML...
<header class="undefined"></header>
I also imported the styles in each component for the corresponding classes
my questions are...why? and how can I fix this? for the class to change when I scroll
thanks! and sorry for my poor english.. greetings from Argentina
Add the props keyword in the header render method... <header className={this.props.classHeader}> etc.
Evrytime you pass a property like classHeader to a component, will be accesible from the current context on a variable called props.
In your example is something like this:
const { classHeader } = this.props;
So, what you need to do is consume from that property on the Header component like this:
render(){
const { classHeader } = this.props;
return(
<header className={`${classHeader}`}>
<nav>
<div>
<ul>{this.navigationItems.items.map((item,i)=><NavItem key={i} data={item} />)}</ul>
</div>
</nav>
</header>
)}}
Related
I have a list of items that are conditionally rendered onto my page- I'd like each of them to have a unique ref based on their ID.
I tried creating a ref for each item within the constructor, but it seems either because the refs are created before the render, or because of my own error- each ref is set to null.
class EmailList extends Component {
constructor(props) {
super(props)
this.state={
emailreports: []
}
props.emailreports.forEach(thing => {
this[`${thing.id}_ref`] = React.createRef()
});
}
componentDidMount(){
this.setState({
emailreports: this.props.emailreports
})
}
render() {
const { emailreports } = this.state;
return (
<div>
<section>
<h5>Email Reports</h5>
<ul>
{
emailreports.length > 0
?
<>
{
emailreports.map((report, i) => {
return <EmailReportItem ref={this[`${report.id}_ref`]} />
})
}
</>
:
<section >
<div className="text-center">You do not currently have access to email report data.</div>
</section>
}
</ul>
</section>
</div>
)
}
}
export default connect(mapStateToProps)(EmailList);
The following image is the result of the above load:
I'm a young dev trying to learn some Reactjs, but I'm having trouble understanding how to configure this Todo app. My goal is to have a button that will add items to the list once entered and submitted. I feel like I'm pretty close to having it figured out.
I've got an App component (parent), button component, and a List component(also a header and item component). the list has a variable that has an empty array for me to add items to, which I reference in my App component.
Here lies the problem. I have an event listener on my button that runs a function that sets the state. I'm logging the list every time I click, which shows that the array is receiving the text inputs and making a new object. However, the DOM is not re-rendering what confuses me even more, is that when I make a slight edit (random semicolon) the DOM renders the items that were entered and logged before I last saved, but remains unresponsive.
What am I missing here? Also, I understand that lifecycle methods like componentDidMount() or componentDidUpdate() may be useful, but I do not fully understand how and where to use them.
export class Button extends Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
const text = document.getElementById('text_field');
const input = text.value;
this.setState(() => {
TodoList.push({id: (TodoList.length+1), name: input})
})
console.log(TodoList)
}
render() {
return (
<div>
<div className='search-container'>
<input className='search' type='text' placeholder='type something...' id='text_field'></input>
</div>
<div className='button-container'>
<button type='submit' className='button-add' onClick={this.handleClick}> New Task </button>
</div>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
todos: TodoList
}
}
render() {
const todoItems = this.state.todos.map(todo => {
console.log(todo.name, todo.id);
return <Item desc={todo.name} key={todo.id} />
})
return(
<div className='wrapper'>
<div className='card'>
<Header numTodos={this.state.todos.length}/>
<div className='todo-list'>
{todoItems}
</div>
<Button />
</div>
</div>
)
}
}
export default App
In your App.js, you should pass a function to <Button />, this technique called function as prop in react. The App.js code should look like below:
class App extends React.Component {
constructor() {
super()
this.state = {
todos: TodoList
}
}
addTodo = (todo) => {
this.setState({ todos: [...this.state.todos, todo] })
}
render() {
const todoItems = this.state.todos.map(todo => {
console.log(todo.name, todo.id);
return <Item desc={todo.name} key={todo.id} />
})
return(
<div className='wrapper'>
<div className='card'>
<Header numTodos={this.state.todos.length}/>
<div className='todo-list'>
{todoItems}
</div>
<Button todosList={this.state.todos} addTodo={(todo) => this.addTodo(todo)} />
</div>
</div>
)
}
In the code for Button.js, you get this function via this.props
export default class Button extends Component {
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
const text = document.getElementById('text_field');
const input = text.value;
this.props.addTodo({id: this.props.todosList.length + 1, name: input })
console.log(this.props.todosList)
}
render() {
return (
<div>
<div className='search-container'>
<input className='search' type='text' placeholder='type something...' id='text_field'></input>
</div>
<div className='button-container'>
<button type='submit' className='button-add' onClick={this.handleClick}> New Task </button>
</div>
</div>
)
}
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.
I have a problem with scroll in React. How can I use scrolling to element in Template? I'd like to scroll to one of the elements in this.props.children by using navigation of Header.
class Template extends React.Component {
handleClick = () => {
const tesNode = ReactDOM.findDOMNode(this.refs.test);
window.scrollTo(0, tesNode.offsetTop);
};
render() {
return (
<div>
<Header click={this.handleClick} ></Header>
{this.props.children}
<Footer></Footer>
</div>
)
}
}
I've made some example here https://codesandbox.io/s/48k35w1107, check this out.
I'm super new to react but excited about its potential. Still getting to grips with the fundamentals of it all so any explanation would be greatly appreciated.
I'm looking to render an 'About' component as the user clicks a button in the 'Nav' component (with the aim to toggle this later down the line).
I've attempted to do it in the simplest way I can think of, but this is obviously very wrong:
class Nav extends React.Component {
renderAbout() {
return (
<About />
);
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.renderAbout()}>About</h2>
</div>
</div>
</div>
);
}
}
Would this have something to do with updating the 'state' of the About component?
Thanks in advance.
You can use state to define if imported component About has to be rendered or not.
class Nav extends React.Component {
state = {
isAboutVisible: false,
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({ isAboutVisible: true }) }>About</h2>
</div>
</div>
{ this.state.isAboutVisible ? <About /> : null }
</div>
);
}
}
You currently do not have "About" component in actual view, you just render it somewhere out there, in the void!
To properly render a component you have to specify its place in JSX expression. Also, as one of the easiest solutions) you probably want to toggle it. So that translates to something like this:
constructor(props){
super(props)
this.state={toggle:false}
}
renderAbout(toggle) {
if(toggle)
return <About />
else return null;
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={() => this.setState({toggle: !toggle})}>About</h2>
</div>
</div>
{this.renderAbout(this.state.toggle)}
</div>
);
}
}
Yes, you have to change state of the component. Changing the state will automatically rerender your component. In your example it should be something like:
class Nav extends React.Component {
state = {
showAbout: false; // initial state
}
renderAbout = () => {
if (!this.state.showAbout) return '';
return (
<About />
);
}
// ES6 priavte method syntax
handleButtonClick = () => {
this.setState({showAbout: true});
}
render() {
return (
<div className="Nav">
<div className="Button-Container">
<div className="Nav-Text About-Button">
<h2 onClick={this.handleBtnClick}>About</h2>
{this.renderAbout()}
</div>
</div>
</div>
);
}
}
You could also consider using for example this package: https://www.npmjs.com/package/react-conditions
Also, remember that there is a rule that each method which listen for an event should start with the "handle" word. Like in may example.