Custom component not getting rendered properly on this basic ReactJS app - javascript

I have a very basic ReactJS app which uses Redux which contains the following components:
PanelMaterialSize > Select
/src/controls/PanelMaterialSize/PanelMaterialSize.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './PanelMaterialSize.scss';
import Select from '../Select/Select';
import { setThemeList } from '../../store/AppConfig/actions';
class PanelMaterialSize extends Component {
componentDidMount() {
this.n = 1;
setInterval(() => {
let themeList = [
{ value: this.n, text: 'Option ' + this.n },
{ value: this.n + 1, text: 'Option ' + (this.n + 1) },
{ value: this.n + 2, text: 'Option ' + (this.n + 2) },
];
this.props.setThemeList(themeList);
this.n += 3;
}, 1000);
}
render() {
return (
<div className="partial-designer-panel-material-size">
<div>
<div className="label-input">
<div className="label">MATERIAL</div>
<div className="input">
<Select data={this.props.themeList} style={{ width: '100%' }} />
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (appState) => {
return {
themeList: appState.appConfig.themeList,
}
}
const mapDispatchToProps = (dispatch) => {
return {
setThemeList: (themeList) => dispatch(setThemeList(themeList)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PanelMaterialSize);
In my opinion the Redux logic is fine because I have tested by doing couple of things.
My problem is that when the render(...) method of: PanelMaterialSize gets called, the component: Select doesn't get rendered with the new data (which changes every one second).
Here you have a Codesandbox.io you can play with (preferable use Chrome):
https://codesandbox.io/s/03mj405zzv
Any idea on how to get its content changed properly?
If possible, please, provide back a new Codesandbox.io with your solution, forked from the previous one.
Thanks!

the problem is here in your select component.
you are passing initially empty array and checking your component with this.state.data props, next time reducer change your this.state.data will not update the data. because you initialize in constructor. constructor only invoke once when component mount.
SOLVED DEMO LINK
The Problem is in your select render method:
render() {
let data = this.state[this.name];
return (
<div className="control-select" {...this.controlProps}>
<div className="custom-dropdown custom-dropdown--grey">
<select className="custom-dropdown__select custom-dropdown__select--grey">
//change data with this.props.data
{this.props.data.length > 0 &&
this.props.data.map((elem, index) => {
return (
<option value={elem.value} key={index}>
{elem.text}
</option>
);
})}
</select>
</div>
</div>
);
}

Related

React: Handling mapped states

I'm very new to coding and trying to figure out an issue I have come across.
I am using axios to pull a json file and store it in a state. (I am also using Redux to populate the form)
Then I am using .map() to dissect the array and show one value from within each object in the array.
example json:
unit :
[
{
designName : x,
quantity : 0,
},
{
designName : y,
quantity : 0,
},
{
designName : z,
quantity : 0,
}
]
I have then added an input to select the quantity of the value mapped and now I want to give that value back to the state, in order to send the entire modified json back to the API with Axios.
I feel like I'm close but I'm unsure what I need to do with the handleQuantity function.
Here's my code:
import React, { Component } from 'react';
import store from '../../redux_store'
import axios from 'axios';
import { Button, Card } from 'react-bootstrap'
import { Link } from 'react-router-dom'
store.subscribe(() => {
})
class developmentSummary extends Component {
constructor(props) {
super(props)
this.state = {
prjName: store.getState()[0].developmentName,
units: []
}
}
componentDidMount() {
axios.get('https://API')
.then(
res => {
console.log(res)
this.setState({
units: res.data.buildings
})
console.log(this.state.units.map(i => (
i.designName
)))
}
)
}
handleQuantity() {
}
render() {
return (
<>
<div className="Text2">
{this.state.prjName}
</div>
<div className="Text2small">
Please select the quantity of buildings from the list below
</div>
<ul>
{this.state.units.map((object, i) => (
<div className="Card-center">
<Card key={i} style={{ width: "50%", justifyContent: "center" }}>
<Card.Body>{object.designName}</Card.Body>
<Card.Footer>
<input
className="Number-picker"
type="number"
placeholder="0"
onChange={this.handleQuantity}
/>
</Card.Footer>
</Card>
</div>
))}
</ul>
Thanks in advance!
You have to pass the change event, unit object and the index to handleQuantity and then paste your changed unit as new object in between unchanged units.
Here is the code:
<input
className="Number-picker"
type="number"
placeholder="0"
onChange={(event) => this.handleQuantity(event, object, i)}
/>;
And the code for handleQuantity
handleQuantity = (event, unit, index) => {
const inputedNumber = +event.target.value; // get your value from the event (+ converts string to number)
const changedUnit = { ...unit, quantity: inputedNumber }; // create your changed unit
// place your changedUnit right in between other unchanged elements
this.setState((prevState) => ({
units: [
...prevState.units.slice(0, index),
changedUnit,
...prevState.units.slice(index + 1),
],
}));
}

Moving elements onclick from one array to another. Content in new array objects empty / not copied?

Good evening,
as a learning project I want to build a simple "Learning Cards" App. The structure is quite simple: you have cards with questions. After a button click, you can show the correct solution. You can also click on "Question solved" to move the learning card to the absolved cards.
I am struggling to realize the "moving the learning card to the absolved" cards part. I have a "questions" array. After "onSolvedClick" the solved card gets copied to the "solved" array which is set as the new solved state.
When I click on the "Frage gelöst" (question solved) button, a new card appears in the solved questions region. The problem is: the new card is empty (without the question / answer). It would be great if someone could help me at this point! I already spent hours on this problem today.
I guess my mistake is within the App.Js code, probably in "onSolvedKlick" or "solveFragen".
Thanks a lot!
App.Js:
import React, {Component} from 'react';
import CardList from './CardList.js';
import { fragen } from './fragen';
import SearchBox from './SearchBox';
class App extends Component { // As Class to access objects
constructor () {
super();
this.state = { // State needed to change state
fragen: fragen,
solved : [] ,
searchfield: ''
}
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
}
onSolvedKlick = (id) => {
console.log("Klick on solved"+id);
var frage = this.state.fragen.filter(function(e) // Bei Klick auf Solved: filtere aus Ursprungsarray das Element mit gelöster iD
{
return e.id === id;
});
console.log(frage);
const newSolvedArray = this.state.solved.slice();
newSolvedArray.push(frage);
this.setState({solved: newSolvedArray});
}
render(){ // DOM rendering
const filteredFragen = this.state.fragen.filter(fragen =>{
return fragen.frage.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
const solveFragen = this.state.solved;
return(
<div className='tc'>
<h1>Learning Cards</h1>
<SearchBox searchChange={this.onSearchChange}/>
<h2>Cards: To be learned!</h2>
<div>
<CardList fragen={filteredFragen} onSolvedKlick={this.onSolvedKlick}/>
<CardList fragen={solveFragen} onSolvedKlick={this.onSolvedKlick}/>
</div>
</div>
)
}
}
export default App;
CardList.js:
import React from 'react';
import Card from './Card';
const CardList = ({fragen, onSolvedKlick}) => {
const cardComponent = fragen.map( (user, i) => {
return(<Card key={i} id={fragen[i].id} frage = {fragen[i].frage} antwort = { fragen[i].antwort} onSolvedKlick = {onSolvedKlick}/>);
}
)
return (
<div>
{cardComponent}
</div>
);
}
export default CardList;
Card.js:
import React, {Component} from 'react';
import 'tachyons';
class Card extends Component {
constructor(props) {
super(props);
this.state = {
frage : props.frage,
showAnswer : false
};
}
_showAnswer = () => {
const before = this.state.showAnswer;
const after = !before;
this.setState({
showAnswer: after
});
}
render() {
return (
<div className ="fl w-50 w-25-m w-20-l pa2 bg-light-red ma3">
<div>
<h2>{this.props.frage}</h2>
{ this.state.showAnswer && (<div>{this.props.antwort}</div>) }
<p></p>
<input type="button" value="Antwort anzeigen" className ="ma2"
onClick={this._showAnswer.bind(null)}
/>
<input type="button" name="solved" value="Frage gelöst" className = "ma2 bg-light-green"
onClick={() =>this.props.onSolvedKlick(this.props.id)}
/>
</div>
</div>
);
}
}
fragen.js (Questions):
export const fragen = [
{
id: 1,
frage: 'What are trends in CPU design?',
antwort: 'Multi-core processors, SIMD support, Combination of core private and shared caches Heterogeneity, Hardware support for energy control',
topic: 'Cloud'
},
{
id: 2,
frage: 'What is typical for multi-core processors?',
antwort: 'Cache Architecture (L1 private to core, L2 private to tile), Cache Coherence',
topic: 'Cloud'
},
{
id: 3,
frage: 'What memory modes exist?',
antwort: 'Flat mode, Cache Mode, Hybrid Mode',
topic: 'Cloud'
},
{
id: 4,
frage: 'What memory modes exist?',
antwort: 'Flat mode, Cache Mode, Hybrid Mode',
topic: 'Cloud'
},
];
Try this on your onSolvedKlick function:
onSolvedKlick = (id) => {
console.log("Klick on solved"+id);
var frage = this.state.fragen.filter((e) => e.id === id);
this.setState({solved: [...this.state.solved, frage]});
}
Try to avoid so many empty lines.
Also keep your code always in english so it's easier for others to understand. I had the luck to be german too :)
Assuming that you want to move the questions from fragen array to solved array, here is how to do that.
onSolvedKlick = id => {
console.log("Klick on solved" + id);
var elementPos = this.state.fragen.map(function(x) {return x.id; }).indexOf(id); // Find the position of the selected item in the fragen
const currentItem = this.state.fragen.splice(elementPos,1)
const newSolvedArray = this.state.solved;
newSolvedArray.push(currentItem[0]);//splice gives an array
this.setState({ solved: newSolvedArray }, function() {console.log(this.state)});
};

Not rendering JSX from function in React

The function is getting the value of a button click as props. Data is mapped through to compare that button value to a key in the Data JSON called 'classes'. I am getting all the data correctly. All my console.logs are returning correct values. But for some reason, I cannot render anything.
I've tried to add two return statements. It is not even rendering the p tag with the word 'TEST'. Am I missing something? I have included a Code Sandbox: https://codesandbox.io/s/react-example-8xxih
When I click on the Math button, for example, I want to show the two teachers who teach Math as two bubbles below the buttons.
All the data is loading. Just having an issue with rendering it.
function ShowBubbles(props){
console.log('VALUE', props.target.value)
return (
<div id='bubbles-container'>
<p>TEST</p>
{Data.map((item,index) =>{
if(props.target.value == (Data[index].classes)){
return (
<Bubble key={index} nodeName={Data[index].name}>{Data[index].name}
</Bubble>
)
}
})}
</div>
)
}
Sandbox Link: https://codesandbox.io/embed/react-example-m1880
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
const circleStyle = {
width: 100,
height: 100,
borderRadius: 50,
fontSize: 30,
color: "blue"
};
const Data = [
{
classes: ["Math"],
name: "Mr.Rockow",
id: "135"
},
{
classes: ["English"],
name: "Mrs.Nicastro",
id: "358"
},
{
classes: ["Chemistry"],
name: "Mr.Bloomberg",
id: "405"
},
{
classes: ["Math"],
name: "Mr.Jennings",
id: "293"
}
];
const Bubble = item => {
let {name} = item.children.singleItem;
return (
<div style={circleStyle} onClick={()=>{console.log(name)}}>
<p>{item.children.singleItem.name}</p>
</div>
);
};
function ShowBubbles(props) {
var final = [];
Data.map((item, index) => {
if (props.target.value == Data[index].classes) {
final.push(Data[index])
}
})
return final;
}
function DisplayBubbles(singleItem) {
return <Bubble>{singleItem}</Bubble>
}
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
json: [],
classesArray: [],
displayBubble: true
};
this.showNode = this.showNode.bind(this);
}
componentDidMount() {
const newArray = [];
Data.map((item, index) => {
let classPlaceholder = Data[index].classes.toString();
if (newArray.indexOf(classPlaceholder) == -1) {
newArray.push(classPlaceholder);
}
// console.log('newArray', newArray)
});
this.setState({
json: Data,
classesArray: newArray
});
}
showNode(props) {
this.setState({
displayBubble: true
});
if (this.state.displayBubble === true) {
var output = ShowBubbles(props);
this.setState({output})
}
}
render() {
return (
<div>
{/* {this.state.displayBubble ? <ShowBubbles/> : ''} */}
<div id="sidebar-container">
<h1 className="sidebar-title">Classes At School</h1>
<h3>Classes To Search</h3>
{this.state.classesArray.map((item, index) => {
return (
<button
onClick={this.showNode}
className="btn-sidebar"
key={index}
value={this.state.classesArray[index]}
>
{this.state.classesArray[index]}
</button>
);
})}
</div>
{this.state.output && this.state.output.map(item=><DisplayBubbles singleItem={item}/>)}
</div>
);
}
}
ReactDOM.render(<Sidebar />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The issue here is ShowBubbles is not being rendered into the DOM, instead (according the sandbox), ShowBubbles (a React component) is being directly called in onClick button handlers. While you can technically do this, calling a component from a function will result in JSX, essentially, and you would need to manually insert this into the DOM.
Taking this approach is not very React-y, and there is usually a simpler way to approach this. One such approach would be to call the ShowBubbles directly from another React component, e.g. after your buttons using something like:
<ShowBubbles property1={prop1Value} <etc...> />
There are some other issues with the code (at least from the sandbox) that you will need to work out, but this will at least help get you moving in the right direction.

How to show different DIV content based on current Index using React?

How can I show different DIV content based on the current index of a slide? This is a component which I'm looping through a MAP and the image, content, and id is inside the DATA object.
What I'm trying to have here to show different HTML/Content based on the currentIndex how can i get this to work?
What am I doing wrong? Currently, it's displaying all the index slides on EACH slide.
Thanks in advance!
import React, { Component } from 'react';
// Components
import QuizSlide from '../Slider/Slide';
// import QuizMain from '../Quiz/QuizMain';
import LeftArrow from '../Arrows/LeftArrow';
import RightArrow from '../Arrows/RightArrow';
import Footer from '../Slider/Footer';
import QuizLogo from 'images/QuizLogo.svg';
// App Styles
import 'sass/root.scss';
export default class QuizSlider extends Component {
// The Constructor
constructor(props) {
super(props);
this.state = {
footerURL: 'http://www.google.nl',
footerText: 'Naar website STC',
copyright: 'Friends For Brands 2018',
currentIndex: 0,
translateValue: 0,
data: [
{index: 1, content: 'Ga voor grenzeloos', image: 'https://images.pexels.com/photos/219014/pexels-photo-219014.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=650&w=940'},
{index: 2, content: 'Sectoren', image: 'https://images.pexels.com/photos/259984/pexels-photo-259984.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=650&w=940'},
{index: 3, content: 'Wat wil jij?', image: 'https://images.pexels.com/photos/355952/pexels-photo-355952.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=650&w=940'},
{index: 4, content: 'Vlogs', image: 'https://images.pexels.com/photos/320617/pexels-photo-320617.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=650&w=940'},
{index: 5, content: 'Belangrijke data', image: 'https://images.pexels.com/photos/1181316/pexels-photo-1181316.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=650&w=940'}
]
}
}
// Functions
PrevSlide = () => {
if(this.state.currentIndex === 0) {
return this.setState({
currentIndex: 0,
translateValue: 0
})
}
// This will not run if we met the if condition above
this.setState(PrevState => ({
currentIndex: PrevState.currentIndex - 1,
translateValue: PrevState.translateValue + (this.slideWidth())
}));
}
NextSlide = () => {
const slideWidth = this.slideWidth();
// Exiting the method early if we are at the end of the images array.
// We also want to reset currentIndex and translateValue, so we return
// to the first image in the array.
if(this.state.currentIndex === this.state.data.length - 1) {
return this.setState({
currentIndex: 0,
translateValue: 0
})
}
// This will not run if we met the if condition above
this.setState(NextState => ({
currentIndex: NextState.currentIndex + 1,
translateValue: NextState.translateValue + -(slideWidth)
}));
}
slideWidth = () => {
return document.querySelector('.QuizSlide').clientWidth
}
// Render
render() {
return (
<div className="QuizSlider">
<div className="QuizLogo">
<img src={QuizLogo}/>
</div>
<LeftArrow PrevSlide={this.PrevSlide} />
<RightArrow NextSlide={this.NextSlide} />
<div className="slider-wrapper" style={{ transform: `translateX(${this.state.translateValue}px)` }}>
{
this.state.data.map((props, index) => (
<QuizSlide key={index} content={props.content} id={index + 1} image={props.image} />
))
}
</div>
<Footer url={this.state.footerURL} text={this.state.footerText} copyright={this.state.copyright} />
</div>
)
}
}
import React from 'react';
const QuizSlide = ({image, content, id}) => {
const currentIndexSlide = id;
if(currentIndexSlide === 1) {
<div className="slide-1">Show this data on 1.</div>
}
if(currentIndexSlide === 2) {
<div className="slide-2">Show this data on 2.</div>
}
if(currentIndexSlide === 3) {
<div className="slide-3">Show this data on 3.</div>
}
return (
<div className="QuizSlide" style={{backgroundImage: `url(${image})`}}>
<div className="QuizSlide--content">
<h2>{content}</h2>
{id}
</div>
</div>
)
}
export default QuizSlide;
In the return section which renders the HTML DOM, you are displaying the entire content. Every time the QuizSlide component is called on iterating the array through a map and hence all the data is displayed.
So, the restriction should be within the render section. The conditional rendering should be something like:
return (
<div className="QuizSlide" style={{backgroundImage: `url(${image})`}}>
<div className="QuizSlide--content">
<h2>{content}</h2>
{id}
{id === '1' &&
<div className="slide-1">
Show this data on 1.
</div>
}
{id === '2' &&
<div className="slide-2">
Show this data on 2.
</div>
}
</div>
</div>
)
Define a variable using let before your if statements, then assign a value to it inside those, displaying that inside your return.
const QuizSlide = ({image, content, id}) => {
const currentIndexSlide = id;
let slide;
if(currentIndexSlide === 1) {
slide = <div className="slide-1">Show this data on 1.</div>
}
if(currentIndexSlide === 2) {
slide = <div className="slide-2">Show this data on 2.</div>
}
if(currentIndexSlide === 3) {
slide = <div className="slide-3">Show this data on 3.</div>
}
return (
<div className="QuizSlide" style={{backgroundImage: `url(${image})`}}>
<div className="QuizSlide--content">
<h2>{content}</h2>
{id}
{slide}
</div>
</div>
)
}
export default QuizSlide;

i18n for array elements of react component

I would like to use universe:i18n for translating my meteor application (using react).
In this component you can see, that I iterate through an array using map() and as the output I would like to get the categories as translations:
imports/ui/components/example.jsx
import React, { Component } from 'react'
import i18n from 'meteor/universe:i18n'
class Example extends Component {
getCategories(index) {
const categories = [ 'one', 'two', 'three' ]; // <-- Get correct translations of these elements
return categories[index - 1];
}
render() {
return (
<div id="content">
{ this.props.sections.map((i) => {
return (
<div>
{ this.getCategories(i.index) }
</div>
);
}) }
</div>
);
}
}
i18n/de.i18.json
{
categories: {
one: 'Eins',
two: 'Zwei',
three: 'Drei'
}
}
I tried to do it with
const T = i18n.createComponent()
class Example extends Component {
getCategories(index) {
const categories = [ 'one', 'two', 'three' ]; // <-- Get correct translations of these elements
return categories[index - 1];
}
render() {
return (
<div id="content">
{ this.props.sections.map((i) => {
return (
<div>
<T>categories[{ this.getCategories(i.index) }]</T>
</div>
);
}) }
</div>
);
}
}
It won't work, because you have to use dot instead of bracker notation, so
<T>categories.{ this.getCategories(i.index) }</T>
Instead of
<T>categories[{ this.getCategories(i.index) }]</T>
But it still won't work, because it will create an children array, but only string is accepted, so use it like this:
<T children={`categories.${ this.getCategories(i.index) }`} />
Source.

Categories

Resources