How to render multiple component inside map function in REACT? - javascript

I am using switch case statement in which i am using two map function. one map function is used for word and other is used for punctuation mark. basically i want to split a word with punctuation mark.
here i face this problem in which everything is going fine but my component in not render. in if else
what should i do call this component whose name is A and B.
</div>
<script type="text/babel">
class A extends React.Component {
constructor(props){
super(props);
this.state={
}
}
render(){
return (
<div>
{this.props.item}hello
</div>
)
}
}
class B extends React.Component {
constructor(props){
super(props);
}
render(){
return(
<div>
{this.props.items}
</div>
)
}
}
class C extends React.Component {
constructor(props){
super(props);
self =this;
this.state = {
cstate:true,
renderdComp:''
}
this.switch =this.switch.bind(this)
this.callComp =this.callComp.bind(this)
}
callComp(item,punc,k){
console.log("item =>",item,"punc =>",punc,"k =>",k);
if(punc == '') {
this.setState({
renderdComp : "A"
})
return <A item={item}/>
} else {
this.setState({
renderdComp : "B"
})
return <B items={item}/>
}}
switch(stateValue) {
if(stateValue) {
let freshItem;
let puncItem;
['a;','as','df','fg'].map( function(item, i) {
[';',':','<'].map( function (punc, j) {
const isFoundPunc = item.indexOf(punc)
if(isFoundPunc > -1) {
console.log(isFoundPunc)
freshItem = item.substr(0,isFoundPunc);
console.log(freshItem)
puncItem = item.substr(isFoundPunc,item.length);
console.log(puncItem)
} else {
freshItem = item
console.log(freshItem+"sec")
puncItem = ''
console.log(puncItem+"sec")
}
})
self.callComp(freshItem, puncItem, i)
})
}
}
render(){
return(
{this.switch(this.state.cstate)}
)
}
}
ReactDOM.render(,document.getElementById("container"));

You can setState here in this function
constructor() {
this.callComp = this.callComp.bind(this)
}
callComp(item,punc,k){
console.log("item =>",item,"punc =>",punc,"k =>",k);
if(punc == '') {
this.setState({
renderdComp : "A"
})
return <A item={item}/>
} else {
this.setState({
renderdComp : "B"
})
return <B items={item}/>
}
This should rerender your component C

Related

Why setState() doesn't update render on React?

I am trying to make a 2D game-board that consists of "Cells" which has a binary state value called isAlive. The user can change this value by clicking on them. This value is indicated to the user using different colors. The Board.js constructor creates every cell with a false value.
The board also has a "Reset" button to reset all Cell's back to false.
The values of the Cells changes correctly when I click on them. When "Reset" button is clicked, I want every Cell's isAlive value to be false. However, when "Reset" button is pressed visually there is no change on the board (The Cell's colors doesn't change). The console.log(this.state.cells); line on handleClick() method on Board.js prints props: Object { isAlive: false } for all Cells. So why the Cell's are not updating visually? What am I doing wrong here?
Board.js
export default class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
cells: []
};
this.rowNum = 10;
this.colNum = 10;
for (let i = 0; i < (this.rowNum * this.colNum); i++) {
this.state.cells.push(<Cell isAlive={false}/>);
}
this.handleClick = this.handleClick.bind(this);
this.getCols = this.getCols.bind(this);
}
getCols() {
let cols = [];
for (let i = this.rowNum; i <= (this.rowNum * this.colNum); i += this.rowNum) {
cols.push(this.state.cells.slice(i - this.rowNum, i).map(function (item, i) {
return <div key={i}>{item}</div>
}));
}
return cols;
}
handleClick() {
const newBoard = this.state.cells;
newBoard.forEach((item, index, arr) => {
arr[index] = <Cell isAlive={false}/>
});
this.setState({
cells: newBoard
}, function () {
console.log(this.state.cells);
}
);
}
render() {
return (<div className="background">
<table className="gameBoard">
<tbody>
<tr>
{this.getCols().map(function (item, index) {
return <td className="col" key={index}>{item}</td>
})}
</tr>
</tbody>
</table>
<button onClick={this.handleClick}> Reset</button>
</div>
);
}
}
The significant parts of the Cell.js:
export default class Cell extends React.Component {
constructor(props) {
super(props);
this.colors = {
dead: '#041b40',
alive: '#b2e8f7',
hover_dead: '#495d76',
hover_alive: '#e6fffd'
};
this.state = {
isAlive: this.props.isAlive,
isMouseDown: false,
isHovering: false
};
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({
isAlive: !this.state.isAlive,
isMouseDown: this.state.isMouseDown,
isHovering: this.state.isHovering
})
}
...
determineColor() {
if (this.state.isAlive && this.state.isHovering) {
return this.colors.hover_alive;
} else if (!this.state.isAlive && this.state.isHovering) {
return this.colors.hover_dead;
} else if (this.state.isAlive && !this.state.isHovering) {
return this.colors.alive;
}else {
return this.colors.dead;
}
}
render() {
return (
<div>
<button className='square'
onClick={this.handleClick}
type='button'
style={{
backgroundColor: this.determineColor()
}}>
</button>
</div>
)
};
}
You need to update the inner state on props change:
export default class Cell extends React.Component {
constructor(props) {
super(props);
this.colors = {
dead: '#041b40',
alive: '#b2e8f7',
hover_dead: '#495d76',
hover_alive: '#e6fffd'
};
this.state = {
isAlive: this.props.isAlive,
isMouseDown: false,
isHovering: false
};
this.handleClick = this.handleClick.bind(this)
}
componentDidUpdate(prevProps, prevState) { //<--HERE
this.setState({isAlive: this.props.isAlive})
}
handleClick() {
this.setState({
isAlive: !this.state.isAlive,
isMouseDown: this.state.isMouseDown,
isHovering: this.state.isHovering
})
}
...
determineColor() {
if (this.state.isAlive && this.state.isHovering) {
return this.colors.hover_alive;
} else if (!this.state.isAlive && this.state.isHovering) {
return this.colors.hover_dead;
} else if (this.state.isAlive && !this.state.isHovering) {
return this.colors.alive;
}else {
return this.colors.dead;
}
}
render() {
return (
<div>
<button className='square'
onClick={this.handleClick}
type='button'
style={{
backgroundColor: this.determineColor()
}}>
</button>
</div>
)
};
You must use the componentDidUpdate
componentDidUpdate(prevProps) {
if (this.props.isAlive!== prevProps.isAlive) {
this.setState({isAlive: this.props.isAlive})
}
}

How can I make a list of integers click on element with corresponding id?

I have a list of ids (integer) and I have multiple components.
After a request to my API, the component receives a list of ids that should already be active.
I want to simulate a click on each element with the same id as the one in my array. I know I can use refs to do that, but I don't undertstand how to make it works with a list of elements.
Here's my code :
import React, { Component } from 'react'
import InterestBox from './InterestBox'
import Axios from 'axios'
export class InterestList extends Component {
constructor(props) {
super(props);
this.state = {pinterests: []}
}
componentDidMount() {
Axios.get('http://localhost:8000/api/interests')
.then((success) => {
this.setState({pinterests: success.data.data.interests});
})
}
componentDidUpdate(prevProps) {
console.log(JSON.stringify(prevProps));
console.log(JSON.stringify(this.props))
if(this.props.alreadyChecked != prevProps.alreadyChecked) {
this.props.alreadyChecked.forEach((item) => {
console.log(item)
})
}
}
render() {
return (
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} icon={pinterest.picture_src} title={pinterest.name} />
})}
</React.Fragment>
)
}
}
export default InterestList
import React, { Component } from 'react'
export class InterestBox extends Component {
constructor(props) {
super(props);
this.images = require('../../img/interests/*.svg');
this.state = {activated: false};
this.interest_box_content = React.createRef();
this.interest_text = React.createRef();
this.handleClick = this.handleClick.bind(this);
this.updateDimensions = this.updateDimensions.bind(this);
}
handleClick() {
this.props.handleClick(this.props.id, this.props.title);
this.setState(prevState => ({
activated: !prevState.activated
}))
}
updateDimensions() {
console.log((window.getComputedStyle(this.refs.interest_box_content).width))
this.refs.interest_text = (window.getComputedStyle(this.refs.interest_box_content).width)
}
render() {
return (
<div className="column is-one-fifth-desktop is-half-touch">
<div className="interest-box">
<div className="interest-box-adjuster">
<div ref={"interest_box_content"} className={"interest-box-content " + (this.state.activated == true ? 'interest-box-activated' : '')} onClick={this.handleClick}>
<img className="interest-icon" src={this.images[this.props.icon]} style={{'height': '50%'}}></img>
<i className="activated-icon fas fa-check"></i>
<span ref={"interest_text"} className="interest-text">{this.props.title}</span>
</div>
</div>
</div>
</div>
)
}
}
export default InterestBox
In the InterestList "componentDidUpdate" method, the value of the item is an integer.
I want to use this integer to "click" on the InterestBox with the corresponding "id".
How can I achieve this ?
You can store an array of elements in one ref, like this:
constructor(props) {
super(props);
this.state = {pinterests: []}
this.pinterestRefs = React.createRef()
}
...
render() {
return (
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} icon={pinterest.picture_src} title={pinterest.name} ref={pinterestRef => this.refs.pinterestRefs.push(pinterestRef)} />
})}
</React.Fragment>
)
}
and then call the click function on each in a componentDidMount function:
componentDidMount() {
if (this.refs.pinterestRefs.length) {
this.refs.pinterestRefs.forEach(pinterestEl => {
pinterestEl.click();
});
}
}
Since this.pinterestRefs is a ref and not an array, the push method is not available. Unfortunately, we do not have a definite length so we can't declare the refs preemptively. However, we can add it to this.refs object and the convert it to an array:
export class InterestList extends Component {
constructor(props) {
super(props);
this.state = {pinterests: []}
}
componentDidMount() {
Axios.get('http://localhost:8000/api/interests')
.then((success) => {
this.setState({pinterests: success.data.data.interests});
})
}
componentDidUpdate(prevProps) {
console.log(Object.values(this.refs)); // Array with all refs
console.log(JSON.stringify(prevProps));
console.log(JSON.stringify(this.props))
if(this.props.alreadyChecked != prevProps.alreadyChecked) {
this.props.alreadyChecked.forEach((item) => {
console.log(item)
})
}
}
render() {
return (
{/*I'm assuming each item has a unique id, if not, create one*/}
<React.Fragment>
{Object.keys(this.state.pinterests).map((interest) => {
var pinterest = this.state.pinterests[interest];
return <InterestBox id={pinterest.id} onClick={this.props.onClick} ref={pinterest.id} icon={pinterest.picture_src} title={pinterest.name} />
})}
</React.Fragment>
)
}
}
export default InterestList;

React calling methods in different conditions

I'm beginner on react and i've written the code below:
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
save() {
this.props.onChange(this.refs.newVal.value, this.props.id);
this.setState({editing: !this.state.editing});
// console.log('save is over');
}
renderForm() {
return (
<div className="note">
<textarea ref="newVal"></textarea>
<button onClick={this.save}>SAVE</button>
</div>
);
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>EDIT</button>
<button onClick={this.remove}>X</button>
</span>
</div>
);
}
render() {
console.log(this.state.editing);
return (this.state.editing) ? this.renderForm()
: this.renderDisplay()
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
In render(), onClick event has a function, that is, if used in this way: {this.add} the following error is created:
Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, ...})
Why? while in the eachNote() method this command is used:
onChange={this.update}
And there was no error.
Someone can tell me the reason? thanks.
The problem is that in the add function you are taking an argument text and setting it in the state so when you call onClick={() => this.add()}, you are not passing any argument to add function and hence in its definition text is undefned and hence state note is set as undefined.
However if you directly call it like onClick={this.add} , the add function receives the event object as a parameter and hence it sets state note to be an event object which you are using to render
onClick={this.add} will pass the click event to this.add.
So what you need to do is either:
onClick={e => this.add('some text')} or similar.
If you want to onClick={this.add} you have to ensure that your add method is: add(event) { ... } instead.
The <Note /> component does not contain a render() method to return anything. Add a render() method and return something.
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
render() {
return (
<div>Render something</div>
)
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>

set class using state if else with react gets state of undefined error

I'm trying to set a class dynamically depending on the pros i send to the component. Somehow i get the error " Cannot read property 'state' of undefined".
I guess that this doesn't exist when i try to set the class of the state as a class? Do i have to rebind it before i use it in the render of the component?
var ReactDOM = require('react-dom');
var React = require('react');
class Button extends React.Component {
constructor(props) {
super(props);
console.log("BUTTON")
console.log(props);
this.state = {
class: "small-button"
};
props.options.map(function (option) {
if (option.Description > 10) {
this.setState({
class: "big-button"
});
}
});
console.log("STATE: " + this.state.class);
}
render() {
if (this.props.options) {
return (<div> {
this.props.options.map(function (option) {
return <div className={ this.state.class === 'big-button' ? 'option-button big-button' : 'option-button small-button'} key={option.Id}> {option.Description}</div>
})
}
</div>
)
} else {
return <div>No options defined</div>
}
}
}
module.exports = Button;
It's a binding issue, you need to bind the function to use this keyword (correct context) inside that.
Use this:
render() {
if (this.props.options) {
return (<div> {
this.props.options.map((option) => {
return <div className={ this.state.class === 'big-button' ? 'option-button big-button' : 'option-button small-button'} key={option.Id}> {option.Description}</div>
})
}
</div> )
} else {
return <div>No options defined</div>
}
}
Check this answer for more detail on arrow function and this keyword
One Suggestion: Don't put logic inside constructor and don't do setState also, use lifecycle method for that. Put that logic inside componentDidMount method or componentWillMount method.
Like this:
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
class: "small-button"
};
}
componentDidMount(){
this.props.options.forEach((option) => {
if (option.Description > 10) {
this.setState({
class: "big-button"
});
}
});
}
render() {
if (this.props.options) {
return (
<div>
{
this.props.options.map((option) => {
return <div className={ this.state.class === 'big-button' ? 'option-button big-button' : 'option-button small-button'} key={option.Id}> {option.Description}</div>
})
}
</div>
)
}else{
return <div>No options defined</div>
}
}
}

flux: view doesn't refresh after store update

I study FLUX and try to write simple example on it. It is about a menu where I can select every single item (it is marked by (*)). So, here is main excerpts of the code:
AppContainer.js:
class AppContainer extends React.Component
{
static getStores() {
return [MenuStore];
}
static calculateState(prevState) {
return {
menu: MenuStore.getState(),
onclick: Actions.onclick
};
}
render() {
return <MenuView onclick={this.state.onclick} menu={this.state.menu.get('list')} cur={this.state.menu.get('cur')} />;
}
}
MenuStore.js
class MenuStore extends ReduceStore{
constructor() {
super(MenuDispatcher);
}
getInitialState() {
// return {list: ["About", "Contacts", "Price", "Home"], cur: 0};
return Immutable.Map({list: ["About", "Contacts", "Price", "Home"], cur: 2});
}
reduce(state, action) {
switch (action.type) {
case ActionTypes.ON_CLICK:
console.log('MenuStone ON_CLICK');
state.cur = action.index;
return state;
default:
return state;
}
}
}
MenuView.js:
class MenuView extends React.Component{
constructor(props){
super(props);
}
render(){
return <ui>{this.props.menu.map((menu, index) => {
var active = (this.props.cur === index) ? true : false;
return <MenuItem onclick={this.props.onclick} key={index} index={index} active={active} label={menu} />;
})}</ui>;
}
}
class MenuItem extends React.Component {
constructor(props) {
super(props);
this.state = {label: props.label};
this.click = this.click.bind(this);
}
click(index) {
console.log('MenuItem click');
this.props.onclick(this.props.index);
}
render() {
var mark = '';
if (this.props.active)
mark = '(*) ';
return <li onClick={this.click}>{mark}{this.state.label}</li>;
}
}
Actions.js:
const Actions = {
onclick(index) {
console.log('Actions.onclick');
MenuDispatcher.dispatch({
type: ActionTypes.ON_CLICK,
index,
});
}
};
When I run the code, it is work fine until I click a menu item. The code
state.cur = action.index;
executes, but nothing changes on the page.
What should I do to update the views?

Categories

Resources