class Headers extends React.Component {
render() {
const selected = this.props.selectedPane;
const headers = this.props.panes.map((pane, index) => {
const title = pane.title;
const klass = index === selected ? 'active' : '';
return (
<li
key={index}
className={klass}
onClick={() => this.props.onTabChosen(index)}>
{title}{' '}
</li>
);
});
return (
<ul className='tab-header'>
{headers}
</ul>
);
}
}
export default class Tabs extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPane: 0
};
this.selectTab = this.selectTab.bind(this);
}
selectTab(num) {
this.setState({selectedPane: num});
}
render() {
const pane = this.props.panes[this.state.selectedPane];
return (
<div>
<h1>Tabs</h1>
<div className='tabs'>
<Headers
selectedPane={this.state.selectedPane}
//onTabChosen={this.selectTab}
panes={this.props.panes}>
</Headers>
<div className='tab-content'>
<article>
hellooooo
{pane.content}
</article>
</div>
</div>
</div>
);
}
}
I'm currently creating a 3 tab section where if you click on a tab, it gives you a new pane.
When looking at the render function I see a custom tag called Headers.
I know it coming from the Headers class at the beginning, but how does that format work? Is that a custom tag we building?
Also when looking at its properties such as onTabChosen, when it is deleted in the render method (for learning purposes) and I click on a selected tab, an error comes up saying
"_this.props.onTabChosen is not a function".
this.props.onTabChosen(index).. was written in the Headers class but not as a function correct?
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.
When looking at the render function I see a custom tag called "Headers".
That is not a custom tag. That is a React Component.
I know it coming from the Headers class at the beginning, but how does that format work?
Headers is either a function or a class (i.e. a constructor function).
The function will be called and the first argument passed to it will be an object with properties and values that match the props on the JSX element.
If you're going to use React then read a tutorial, this is very introductory level stuff for the framework.
It is covered very early on in both the MDN tutorial and the official React tutorial.
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.
It was declared, just not in the piece of code you shared.
I've used stack overflow for the last two years since I picked up programming, but I've never actaully asked a question directly. However, recently I've been running into a problem with React which has stumped me for the last week, and it feels like one of those problems a pro will look at and instantly know the answer to.
My code: https://codepen.io/gooeyWolf/pen/xxZGxGq
<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>
<body>
<div id="root"><div id="shit"></div></div>
<script src="https://unpkg.com/react#16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.12.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone#7.8.3/babel.js"></script>
<script type="text/babel">
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',};
this.handleChange = this.handleChange.bind(this);
}
NextThing(){
const element = <h1>Hello, world!</h1>;
const divy = document.body.getElementById("div2");
divy.appendChild(element);
//document.body.appendChild(element);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleEnter(event){
if(event.key === 'Enter'){
this.NextThing();
}
}
render() {
return (
<div>
<span>Are you writing for a protagonist, antagonist, love interest, or secondary character? <br /></span>
<input name="flip" id="lucky" value={this.state.value} onChange={this.handleChange} onKeyDown={this.handleEnter} />
<div id="div2"></div>
</div>
);
}
}
//onChange={this.handleChange}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
</script>
</body>
The app Im building is pretty simple. Basically, the app asks you a question, you type in your answer, and it displays a new block of text and a new input box to type your next answer, while leaving previous text on the screen.
So far, when you type something and press enter, the programs handles the enter, and calls another function to display the next block of text.
At first, i tried using return/render statements, but this cause the page to refresh, which would be fine, but the new elements I wanted to display doesn't show up. The React.JS course I'm taking advises against using creatElement statements for every new element required, so I'm working with appendChild, which for some reason this runs into errors.
I feel like there has to be a more simple solution to this, and a more reusable solution too. Ideally, it would be able to handle a refresh and keep the previously added elements.
I wrote this program in python first, with print and input statements, and it was so easy, but I spent the last week trying figure out the right way with React, but all my solutions seem unnecessarily complicated. There has to be a better way...
Thank you guys so much, this helps more than I can express.
Stack Overflow the best (:
In React, the common DOM manipulation functions (appendChild, innerHtml etc) are not the "best" way to do this, there is a more usable solution as you said. What you can do, is take advantage of the way JavaSript expressions can be renderered inside the HTML (That is, those that are inside with {{variable}} with curly braces) and combine it with this.state.
First in this.state, define an array div2Children which will contain all the text that is typed into the input
// This is inside the constructor
this.state = {value: '', div2Children: []};
Then you can map through each element and convert them in HTML.
render() {
return (
<div>
<span>Are you writing for a protagonist, antagonist, love interest, or secondary character? <br /></span>
<input name="flip" id="lucky" value={this.state.value} onChange={this.handleChange} onKeyDown={this.handleEnter} />
<div id="div2">
{/* when the array changes this page will also change the HTML */}
{this.state.div2Children.map(child => (
<h1>{child}</h1>
))}
</div>
</div>
);
}
Then finally in your NextThing method you'll have to change with this.setState the div2Children array to have all the previous elements but also your newly typed (appending the last element but creating a new array)
NextThing(){
this.setState({div2Children : [...this.state.div2Children, this.state.value]});
}
Note that you will also have to bind NextThing and handleChange. Here is a working version
<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>
<body>
<div id="root"><div id="shit"></div></div>
<script src="https://unpkg.com/react#16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.12.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone#7.8.3/babel.js"></script>
<script type="text/babel">
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: '', div2Children: []};
// Bind all the functions
this.NextThing = this.NextThing.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleEnter = this.handleEnter.bind(this);
}
NextThing(){
this.setState({div2Children : [...this.state.div2Children, this.state.value]});
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleEnter(event){
if(event.key === 'Enter'){
this.NextThing();
}
}
render() {
return (
<div>
<span>Are you writing for a protagonist, antagonist, love interest, or secondary character? <br /></span>
<input name="flip" id="lucky" value={this.state.value} onChange={this.handleChange} onKeyDown={this.handleEnter} />
<div id="div2">
{this.state.div2Children.map((child, index) => (
<h1 key={index}>{child}</h1>
))}
</div>
</div>
);
}
}
//onChange={this.handleChange}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
</script>
</body>
Also notice here that I am also setting a key attribute in the snippet above. You can find more about that here, I just provided a simple solution which should be fine.
Firstly, you can't access to the DOM elements like that in React. Check the refs react API. Anyway, I think you should reverse your reflection here and avoid those dom-manipulation (if you can).
The data should be enough to display questions and answers.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {
step: 1,
values: [''],
questions: [
{
id: 1,
label: 'Are you writing for a protagonist, antagonist, love interest, or secondary character?',
},
{
id: 2,
label: 'Are you happy?',
}
// other questions
]};
}
NextThing = (next) => {
this.setState((prev) => ({step: prev.step+1, values: [...prev.values, '']}));
}
handleChange = (index, event) => {
event.persist()
this.setState(state => {
const values = state.values.map((item, j) => {
if (j === index) {
return event.target.value;
} else {
return item;
}
});
return {
values,
};
});
}
handleEnter = (index, event) => {
if(event.key === 'Enter' && this.state.step < this.state.questions.length){
this.NextThing(index);
}
}
render() {
const { questions, step, values } = this.state;
return (
<div>
{questions.slice(0, step).map((question, index) => (
<div key={question.id}>
<span>{question.label}<br /></span>
<input
name={"answer"+question.id}
id={"answer"+question.id}
value={values[index]}
onChange={e => this.handleChange(index, e)}
onKeyDown={e => this.handleEnter(index, e)} />
</div>
))}
</div>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
Let me know if I misunderstood something or if it helps :D
As others wrote, you shouldn't try to manipulate DOM like regular javascript in React.
So; we shouldnt manipulate elements like regular js, but what we can do is we can change stuff inside HTML with Refs and States. You can use ref's to give an element some kind of "id" in React way, and change its properties. State is one of the key elements of React and its quite powerful.
https://reactjs.org/docs/state-and-lifecycle.html
We can toggle on and off divs, inputs and other elements visibility, innerTexts and etc.
https://reactjs.org/docs/hooks-state.html
I will use a State Hook to change displaying of a div in here:
const [testState, setTestState] = useState(false)
...
return (
<>
<h1>Example Page</h1>
{testState ? <input placeholder="Example Input" /> : null}
</>
)
If my testState is true; an input box will show up; and if its false nothing will. We can use setTestState(true) -or false- now somewhere in our code to change visibility rendered html block.
Now, not sure if i understood your problem and phyton code correctly (i probably didnt), i quickly made a demo with using state's to toggle an elements visibility.
https://codesandbox.io/s/zealous-taussig-0ctif
I am new to react and am trying to toggle a body class using two different buttons. One is supposed to add a class using an onClick event and the other is supposed to remove the class. Below is an example of my code.
Right now in the console I can see the event fire twice but the class remains. As I stated I am new to React so I know I may be doing this incorrectly.
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
You are trying to modify the dom directly like you would with vanilla js or JQuery, but this is not how react is meant to be used. React creates a virtual dom that you create and manage, and then react handle changing the page for you.
I recommend following a guide like this one to learn basic setup and concepts (skip to the part where he uses JSX).
I can further point you in the right direction if you show your whole component file.
You want to toggle a className prop value in the React way.
The React way is having a state prop and having a handler function that will toggle the state value, rather than manipulating the DOM node directly (the way you're doing it).
I would suggest you to take a look at React Main Concepts: Handling events and later once you feel a little bit more comfortable to read about Virtual DOM and Reconciliation in React.
Here's how can you do it:
const { classNames } = window
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
isToggled: true
}
this.toggleClass = this.toggleClass.bind(this)
}
toggleClass() {
const { isToggled } = this.state
this.setState({
isToggled: !isToggled
})
}
render() {
const { isToggled } = this.state
const className = classNames({
'body-fixed': isToggled
})
return <div className={className}>
<div>Current `className`: <b>{ className }</b></div>
<button onClick={this.toggleClass}>Toggle class</button>
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://unpkg.com/classnames#2.2.6/index.js"></script>
<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="container"></div>
I was able to use the code I listed earlier. I had my onClick events positioned incorrectly. Here is an example of the code I used:
bodyFixed() {
document.body.classList.add('body-fixed');
}
bodyRelative() {
document.body.classList.remove('body-fixed');
}
<Toggle>
{({on, getTogglerProps, setOn, setOff, toggle, showAlert}) =>
<div>
<button className="search-icon-top" {...getTogglerProps()}>{on ? <img className="times" onClick={this.bodyRelative} src={require('img/times.svg')} alt=" " /> : <i className="fa fa-search" onClick={this.bodyFixed}></i>}</button>
<div>
{on ? <TodaySearchBox /> : ''}
</div>
</div>}
</Toggle>
This is just a start for now. Thank you for the input.
EDIT: I am open to suggestions. Like I said I am new to React.
I have tried finding the answer to this on StackOverflow and there are some related posts (e.g. React Child Component Not Updating After Parent State Change) but I want to understand why this is not working...
I have a React application that will display a layout of character cards (that is, each card displays a different character). It uses a child component, CharacterBoard, that lays out the CharacterCards, which would be a grandchild component. I pass the characters down from the App to the CharacterBoard as props, and CharacterBoard in turn maps these out the CharacterCards.
The problem is that I want the state of the character to change when I click on one of them. Specifically, I want the revealed field to change. However, even though the state change is reflected in the array of characters in the App (that is, the revealed field changes correctly), and the change is reflected in the array of characters in CharacterBoard, but not in CharacterCard. In fact, my mapping does not seem to be called at all in CharacterBoard when the props change.
Do I need to use something like getDerivedStateFromProps in CharacterBoard and set the state of that component and then use the state to map the values down to CharacterCard? If so, why?
In short (tl;dr), can you pass props on down through the component chain and map them out along the way and still have all changes reflected automatically?
Thanks for any guidance.
If it helps, the render method of my App is
render() {
const {state: {characters}} = this
return (
<div>
<header>
</header>
<main>
<CharacterBoard
onCardSelected={this.onCardSelected}
rowSize={logic.ROW_SIZE}
characters={characters}
cardSize={this.CARD_SIZE}/>
</main>
</div>
);
}
that of CharacterBoard is
render() {
const {props: {characters, rowSize, cardSize,onCardSelected}} = this
const rowUnit = 12 / rowSize
const cardLayout = characters
.map((character, i) => (
<Col xs={6} sm={rowUnit} key={character.name}>
<CharacterCard
onCardSelected = {onCardSelected}
key={i + Math.random()}
character={character}
cardSize={cardSize}
/>
</Col>
)
)
return (
<div>
<Container>
<Row>
{cardLayout}
</Row>
</Container>
</div>
)
}
and finally CharacterCard has this render method
render() {
const {props: {character, cardSize}} = this
const {thumbnail, revealed} = character
const imgURL = `${thumbnail.path}/${cardSize}.${thumbnail.extension}`
const topCardClass = classNames('characterCard__card-back', {'characterCard__card-back--hidden': revealed})
console.log(revealed)
return < a href="/#" onClick={this.onCardSelected}>
<div className='characterCard__card'>
<div className={topCardClass}>
<img src="/images/card_back.png" alt=""/>
</div>
< div className='characterCard__card-front'>< img alt=''
src={imgURL}/>
</div>
</div>
</a>
}
Doh! A simple forgetting to setState in App. Knowing that it should work made me go back through the code one more time and see that, indeed, it was a stupid error on my part.
I'm relative new to react and I need to create a character counter component (CharacterCounter) for a input field.
This question is not specific to this component (character counter) - but is a generic question about components best practices in general.
I can implement in two ways (as far I know - if there are better ways I'm happy to hear about it):
1) Wrapping the component and have the input field as a child - The component will insert the span (for showing the counter) after the input field
<CharacterCounter maxLength={50}>
<input type="text">
</CharacterCounter>
and
const input = this.container.firstChild
input.addEventListener('keyup', function() { ... });
advantage: I can have multiple components for the same input - if I need extra functionality (components) for this input.
disadvantage: If the input for some reason stop being the first child of this component - stop working/ fragile
2) To create a generic component which will render the input and the counter on the render() function
like:
<CharacterCounter />
render() {
return (
<input type="text">
<span>{this.state.count}</span>
)
advantage: Not fragile - not relying on the first child
disadvantage: Not sure is possible to have other component for the same input - let's say I need another component for tracking every time the user type/ or focus/ or blur the field
What is the best practices?
Surely the second approach is better as it is not directly interfering with DOM elements.
If you wanted to have access to DOM elements, still it's better to use refs.
disadvantage: Not sure is possible to have other component for the
same input - let's say I need another component for tracking every
time the user type/ or focus/ or blur the field
You will get around that easily just with props.
You could use second approach with components with state and then use composition to extend that component or create more “special cases” of that component.
let {Component} = React;
class Input extends Component {
constructor(props) {
super(props);
this.state = {count: 0}
}
render() {
return <div>
<input
{...this.props}
onChange={() => {
let count = this.refs.inputCount.value.length;
this.setState({count})
}}
type="text"
maxLength="50"
ref="inputCount" />
<span> {this.state.count} / 50</span>
</div>
}
}
class FocusInput extends Component {
render() {
return <Input
onFocus={() => {
console.log('Focus')
}} />
}
}
class App extends Component {
render() {
return <div>
<Input />
<FocusInput />
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://unpkg.com/react#16.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.1.0/umd/react-dom.development.js"></script>
<div id="app"></div>