Render Random Item from State React.JS - javascript

Given some items stored in state, I want to be able to click a button and display a random item from that array. So far it only works on the first click and then it displays the same one letter after the first click.
What exactly is going on?
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
notes: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked ? this.state.notes : ''}</h1>
</div>
)
}
}

Add selected note handling
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
selectedNote: null,
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
selectedNote: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked && this.state.selectedNote}</h1>
</div>
)
}
}

You're overwriting the notes array in state in your handleClick method. Try using a different key (something like activeNote) in handleClick, then use that in your render method rather than this.state.notes.

So I think I solved it.
I ended up adding another state key randomWord and setting it equal to ''.
I then set the state of randomWord to that of this.state.notes when randomized.
Finally, I rendered this.state.randomWord
No clue if that's the correct approach but it's a working solution, lol.

Related

React this.state.addroom.map is not a function

So I am trying to gen a div with a button onClick of a button but I get an error that is stopping me from doing this.
Error: TypeError: this.state.addroom.map is not a function
But I saw that when I click my button once it doesn't show the error but it doesn't generate the div with the button either.
Here is my code:
import React, { Component } from 'react';
import Select, { components } from 'react-select';
import styles from '../styles/loginsignup.css'
import axios from 'axios'
import nextId from "react-id-generator";
export default class AccomodationInfo extends Component {
constructor() {
super();
this.state = {
accomcate: null,
addroom: ['one'],
isLoading: true,
}
}
handleClick = event => {
const htmlId = nextId()
event.preventDefault()
const addroom = this.state.addroom
this.setState({ addroom: htmlId })
return (
<div>
{this.state.addroom.map(addrooms => (
<button key= {addroom.id} className={addrooms.modifier}>
{addrooms.context}
</button>
))}
</div>
);
}
render() {
return(
<div>
<button onClick={this.handleClick}>Add</button>
</div>
)
}
}
}
Anyone knows what causes it and how we can fix it?
There are a few things off with your code.
First of all the addroom in your state is a string array in your constructor, but in the handleClick method you set it like this.setState({ addroom: htmlId }) which will set it to a string and on a string type the map function is not defined, hence the error.
You should add an item to the array like this.setState({ addroom: [...this.state.addroom, htmlId] })
Secondly, in your handleClick you shouldn't return jsx, if you wan to render data for your addroom array, you should do it in the render method, and in the handleClick you should just modify the addroom state variable.
You can achieve this like:
render() {
return (
<div>
<button onClick={this.handleClick}>Add</button>
{this.state.addroom.map((addroom) => (
<button>{addroom}</button>
))}
</div>
)
}
Lastly, your addrom variable is a string array only, so you can't access id, modifier and context in an item in that array.

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

Render a component from another class using function

So, I have a class like this:
class Blah extends Component {
constructor(props) {
super(props);
}
handleComponent = (event) => {
let divid = event.target.getAttribute('id');
if (divid === 'col') {
// I want to render component by this condition
} else if (divid === 'ro') {
// or I want to render component by this condition
} else {
//or I want to render component by this condition
}
};
render() {
const { classes } = this.props;
return (
<div>
<div id = 'col' onClick={this.handleComponent}>Sheep</div>
<div id = 'ro' onClick={this.handleComponent}>Cow</div>
<div id = 'ball' onClick={this.handleComponent}>Dog</div>
{ I want to render my component here after click }
</div>
);
}
}
I have another class written on top of this:
class Flow extends Component {
constructor(props){
super(props);
};
render() {
return(
<div style={{background:'somecolor'...blah blah}}>Clap</div>
);
}
}
And I am Passing this by:
var foo = withStyles(styles)(Flow)
I have tried returning components but I am not getting anywhere.
I can use ternary operator but it still will render only one of two but I have three component have three design for each of them.
I want to render one of them to render on some condition as stated above.
If I use states that for toggle that will too have two components for render. Don't go on the code, this is made up, So any Ideas ? Fragments ? Any help will be appreciated. Thank you.
To render component by condition simply use switch statement.
In my example we use state to store current active component.
renderMyComponent method takes care of rendering one of three possible components.
handleChange method changes current state and triggers new render of App component.
This example use class properties plugin.
renderMyComponent = () => {
means autobind and is the same as using in constuctor method
this.renderMyComponent = this.renderMyComponent.bind(this);
Working example:
const ComponentOne = () => <div>Hi, i am component one</div>;
const ComponentTwo = () => <div>Hi, i am component two</div>;
const ComponentThree = () => <div>Hi, i am component three</div>;
class App extends React.Component {
state = { current: 0 }
renderMyComponent = () => {
// Our switch conditional render
switch(this.state.current) {
case 0:
return <ComponentOne />;
case 1:
return <ComponentTwo />;
case 2:
return <ComponentThree />;
default:
return null;
}
}
handleChange = (event) => {
// We are looking for data-trigger attribute
// In this example we expect type number but trigger holds string
// That's why we 'cast' to a number using Number()
const current = Number(event.target.dataset.trigger);
// Sets new state of current component and triggers new render
this.setState({ current })
}
render() {
return (
<div>
<div>
Pick component to render
<button
type="button"
data-trigger="0"
onClick={this.handleChange}
>
Render 1
</button>
<button
type="button"
data-trigger="1"
onClick={this.handleChange}
>
Render 2
</button>
<button
type="button"
data-trigger="2"
onClick={this.handleChange}
>
Render 3
</button>
</div>
{this.renderMyComponent()}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<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="root"></div>
Reviewing your code:
You don't need constructor here.
...
constructor(props){
super(props);
}
...

Tie an action to a state change in pure React

Learning React so might be a bit nooby question. Consider this code:
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle(on) {
this.setState({
isOn: on
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: false
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
});
this.props.handleToggle(on);
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
As you can see currently when the button inside the Container is clicked handleButtonToggle() is fired which changes the state of the button itself and then calls a function to change the state of the parent Container. Is this the React way to do it? At the moment Container state is changed when the function handleButtonToggle is fired. Ideally I would want Container state to be dependent on MyButton state directly (cuz maybe in future there will be ways to set button state other than through handleButtonToggle, and I don't want to manually call this.props.handleToggle every time the state changes.). In other words is there a way to do something like this.props.handleToggle(this.state.pressed) in the button component when its state changes.
Codepen
After reviewing the code, a better way to write the Button component is to make it a controlled component. If the container component requires to maintain the pressed state, a controlled button component would receive the pressed state from the container component as props.
<MyButton pressed={this.state.pressed} onToggle={this.handleToggle} />
And the render method of the Button component should be:
render() {
return (
<div>
<button onClick={this.props.onToggle}>
{this.props.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
The actual button toggle will be done in the handleToggle method of the container component:
handleButtonToggle() {
let { pressed } = this.state;
pressed = !pressed;
this.setState({
pressed
});
}
You can either pass a callback as the second argument to setState or implement componentDidUpdate on your component to wait for state changes. The smallest change would be the former:
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
}, () => {
this.props.handleToggle(on);
});
}
or with componentDidUpdate:
componentDidUpdate(prevProps, prevState) {
if (prevState.on !== this.state.on) {
this.props.handleToggle(this.state.on);
}
}
Here is a working codepen: https://codepen.io/damien-monni/pen/XRwewV.
I tried to keep as much as I can of your code so you can focus on really needed changes.
You need to create a controlled component. The state of your button will be stored in the container and pass by props to the child MyButton component.
/*
* A simple React component
*/
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.setState({
isOn: !this.state.isOn
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton pressed={this.state.isOn} handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: this.props.pressed
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
this.props.handleToggle();
}
componentWillReceiveProps(nextProps) {
if (nextProps.pressed !== this.props.pressed) {
this.setState({ pressed: nextProps.pressed });
}
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
You need to do it because you want to share a state between two of your components. This is documented here.
I let you look at the codepen and ask your questions about it. ;)

React component could not sync props which created by dynamic ReactDOM.render

When I use React+Redux+Immutable, I get an issue: the component created by dynamic way, when the props change, component not rerender.
Is it React bug?
I deleted business code, just React code here: http://codepen.io/anon/pen/GoMOEZ
or below:
import React from 'react'
import ReactDOM from 'react-dom'
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'tom'
}
}
dynamic() {
ReactDOM.render(<B name={this.state.name} changeName={this.changeName.bind(this)} type={false}/>, document.getElementById('box'))
}
changeName() {
this.setState({
name: 'tom->' + Date.now()
});
}
render() {
return <div>
top name: {this.state.name}
<B name={this.state.name} changeName={this.changeName.bind(this)} type={true}/>
<div id="box"></div>
<button onClick={this.dynamic.bind(this)}>dynamic add component</button>
</div>
}
}
class B extends React.Component {
render() {
return <div>
{this.props.type ? '(A)as sub component' : '(B)create by ReactDOM.render'}
- name:【{this.props.name}】
<button onClick={this.props.changeName}>change name</button>
</div>
}
}
ReactDOM.render(
<A/>,
document.getElementById('example')
);
It is not a bug, it is just not React-way to do what you want. Each call to A.render will overwrite <div id="box">...</div> deleting elements added by A.dynamic.
More idiomatic way is to add some flag, set it in onClick handler and use it in A.render to decide if <div id="box"> should be empty or not.
See edited code on codepen: http://codepen.io/anon/pen/obGodN
Relevant parts are here:
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'tom',
showB: false // new flag
}
}
changeName() {
this.setState({
name: 'tom->' + Date.now()
});
}
// changing flag on button click
showB() {
this.setState({showB: true})
}
render() {
// `dynamic` will contain component B after button is clicked
var dynamic;
if(this.state.showB) {
dynamic = <B
name = {this.state.name}
changeName = {this.changeName.bind(this)}
type = {false} />
}
return <div>
top name: {this.state.name}
<B name = {this.state.name}
changeName = {this.changeName.bind(this)}
type = {true}/>
<div>{dynamic}</div>
<button onClick = {this.showB.bind(this)}>
dynamic add component
</button>
</div>
}
}
Update
You can still use your approach, but you need to manually update dynamically created component.
E.g. you can manually rerender it in changeName function.
changeName() {
this.setState({
name: 'tom->' + Date.now()
}, this.dynamic.bind(this));
}
Note, that this.dynamic is passed as a second argument to this.setState this ensures that it will be called when state is really updated. Just adding this.dynamic() after this.setState({...}) will use not-yet-updated state.
Codepen here: http://codepen.io/anon/pen/EPwovV

Categories

Resources