I have an object:
import React, {Component} from 'react';
import ProgressBar from "./ProgressBar";
class Languages extends Component {
state = {
languages: [
{id: 1, value: "XXX", xp: 1.5},
{id: 2, value: "CCC", xp: 1},
{id: 3, value: "AAA", xp: 2}
]
}
render() {
let {languages} = this.state;
const levels = ['Test', 'Bad', 'Sorry']
return (
<div className="languages">
<ProgressBar
programming={languages}
className="languagesDisplay"
levels={levels}
title="Languages"
/>
</div>
);
}
}
export default Languages;
import React from 'react';
const ProgressBar = (props) => {
return (
<div className={props.className}>
<h3>{props.title}</h3>
<div className="years">
<span>Experiences</span>
props.levels.map((level) => {
<span>level</span>
})
</div>
<span>level</span> return props.levels.map((level) =>level)
how can i display the const ['Test', 'Bad', 'Sorry'] from Languages.js in a <span> in a different React file?
Edit after seeing your response above: If the issue is just that the above code isn't working, here are a couple of things to check.
Inside of ProgressBar you've got a couple of errors in your JSX. First, you need curly braces around your JavaScript interpolation and secondly, you're not returning anything in your .map() function. If you were using parentheses it would be an implicit return, but with the curly braces you need a return statement. Try this instead:
import React from 'react';
const ProgressBar = (props) => {
return ( <div className={props.className}>
<h3> {props.title} </h3>
<div className ="years">
<span> Experiences </span>
{props.levels.map((level) => {
return (<span>{level}</span>)
})
} </div>
)};
My initial answer, which still may be helpful for understanding what's going on:
It's not entirely clear what you want to do, but here are a couple of things that might be helpful.
What's happening in your code above is that the levels variable, which is an array of strings, is being passed down from the parent component Languages into the child component ProgressBar via the props object.
When ProgressBar is called inside of Languages, it's properties (or props) are set (programming, className, levels, title).
The levels={levels} part means that the prop levels on ProgressBar is being set to the variable levels (the array of strings).
Inside of ProgressBar all of those properties are accessible in the props object that's passed as an argument. That's why you're able to access that array of strings with props.levels.map() which will map the array of strings however you tell it to (in this case by printing each individual item within a <span> tag).
So, with that understanding of what's happening here, here are a couple of things you could do to access the levels variable elsewhere in another file.
If levels is a constant that you want to access in multiple places, you could move it outside of the body of your Languages component and export it to use it in other places.
That could look like:
import React, {
Component
} from 'react';
import ProgressBar from "./ProgressBar";
export const levels = ['Test', 'Bad', 'Sorry']
class Languages extends Component {
state = {
languages: [{
id: 1,
value: "XXX",
xp: 1.5
},
{
id: 2,
value: "CCC",
xp: 1
},
{
id: 3,
value: "AAA",
xp: 2
}
]
}
render() {
let {
languages
} = this.state;
return ( <
div className = "languages" >
<
ProgressBar programming = {
languages
}
className = "languagesDisplay"
levels = {
levels
}
title = "Languages" /
>
<
/div>
);
}
}
export default Languages;
By exporting it from the top level, you could import it in another file exactly as it is.
import { levels } from '/insert-first-file-location-here'
Another option is to pass the levels variable into another component as a prop. This way if levels gets changed at the top level, those changes will drill down into subsequent components.
import React, {Component} from 'react';
import ProgressBar from "./ProgressBar";
class Languages extends Component {
state = {
languages: [
{id: 1, value: "XXX", xp: 1.5},
{id: 2, value: "CCC", xp: 1},
{id: 3, value: "AAA", xp: 2}
]
}
render() {
let {languages} = this.state;
const levels = ['Test', 'Bad', 'Sorry']
return (
<>
<div className="languages">
<ProgressBar
programming={languages}
className="languagesDisplay"
levels={levels}
title="Languages"
/>
</div>
<AnotherComponentThatUsesLevels
levels={levels} />
</>
);
}
}
export default Languages;
And then
import React from 'react'
export const AnotherComponentThatUsesLevels (props) => {
return (
<>
{/* do something with levels here, maybe map them like before*/}
{props.levels.map((level) => (<span>{level}</span>)}
</>
)
}
Does that help understand what's happening in the example and give you a couple of ways you could use that variable in another location?
You need to export that certain constant from your file like that:
import React, {
Component
} from 'react';
import ProgressBar from "./ProgressBar";
export const levels = ['Test', 'Bad', 'Sorry']
class Languages extends Component {
state = {
languages: [{
id: 1,
value: "XXX",
xp: 1.5
},
{
id: 2,
value: "CCC",
xp: 1
},
{
id: 3,
value: "AAA",
xp: 2
}
]
}
render() {
let {
languages
} = this.state;
return (
<div className="languages">
<ProgressBar
programming={languages}
className="languagesDisplay"
levels={levels}
title="Languages"
/>
</div>
);
}
}
export default Languages;
After it, you need to import it in the file where you want to access it:
import {levels} from '/path/to/file';
Related
I'm trying to use an array of dictionaries in python as arguement to a custom dash component and use it as array of objects
in python :
audioList_py = [
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_1.jpg',
"musicSrc":
'link_1.mp3',
},
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_2.jpg',
"musicSrc":
'link_2.mp3',
},
... etc
]
in Javascript:
audioList1_js = [
{
name: "random",
singer: 'waveGAN\'s music',
cover:'link_1.jpg',
musicSrc: 'link_1.mp3',
},
{
name: "random",
singer: 'waveGAN\'s music',
cover: 'link_2.jpg',
musicSrc: 'link_2.mp3',
},
... etc
]
Here is snippet of javascript code of the dash custom component:
export default class MusicComponent extends Component {
render() {
const {id, audioLists} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audio_list}/>,
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
audios: PropTypes.array,
id: PropTypes.string,
};
And using the generated component in python:
app = dash.Dash(__name__)
app.layout = html.Div([
music_component.MusicComponent(audios=audioList_py),
html.Div(id='output'),
... etc
])
But I got :
TypeError: The `music_component.MusicComponent` component (version 0.0.1) received an unexpected keyword argument: `audios`Allowed arguments: id
What I am doing wrong ?
Any help or advice will be appreciated, Thanks a lot.
Make sure you run npm run build after you make a change to your custom React component. With those proptypes you shouldn't get that error. If I remove the audios proptype I can reproduce that error.
Besides that you pass a value to the audios property:
music_component.MusicComponent(audios=audioList_py)
but you try to retrieve audioLists from props:
const {id, audioLists} = this.props;
Change this to:
const {id, audios} = this.props;
Demo
export default class MusicComponent extends Component {
render() {
const {id, audios} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audios} />
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
id: PropTypes.string,
audios: PropTypes.array,
};
Issue fixed, I should run : npm run build:backends to generate the Python, R and Julia class files for the components, but instead I was executing npm run build:js and this command just generate the JavaScript bundle (which didn't know about the new props).
And set the audios property in the component to be like so:
MusicComponent.defaultProps = {audios: audioList1};
MusicComponent.propTypes = {
id: PropTypes.string,
audios: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)).isRequired
};
I created an array of objects with properties of name and birth_date. I then create a Person component and map out the array using props. I'd like to make the name editable so I put the props into an input value. I am trying to use hooks to be able to update that objects name, however, what I currently have only returns an empty value and then edits all values in the table. Any way to specify to only edit that particular objects name and update the array?
NewTable.js
import React, { useState } from 'react';
import './list.css';
import TableHeader from './TableHeader';
import PersonItem from './PersonItem';
import { people } from './people.js';
function NewTable() {
const [itemDetails, editItemDetails] = useState(people.name)
function newPeople(people) {
return (
<PersonItem
name={itemDetails}
date={people.birth_date}
edit={e => editItemDetails(e.target.value)}
/>
);
}
return(
<div id="task-group">
<table className="task-list">
<TableHeader />
<tbody>
{people.map(newPeople)}
</tbody>
</table>
</div>
)
}
export default NewTable;
PersonItem.js
import React from 'react';
import './list.css';
function PersonItem(props) {
return(
<tr>
<td><input type="text" value={props.name} onChange={props.edit}/></td>
<td><input type="text" value={props.date}/></td>
</tr>
)
}
export default PersonItem;
people.js
const people = [
{
name: "John Smith",
birth_date: '01/01/1991',
},
{
name: "Dwayne Johnson",
birth_date: '03/05/1992',
},
]
export { people };
Here is one possible solution:
Have a unique id for every person:
const myPeople = [
{
id: "1",
name: "John Smith",
birth_date: '01/01/1991',
},
{
id: "2",
name: "Dwayne Johnson",
birth_date: '03/05/1992',
},
]
Then pass down this function to the PersonItem component so they can call up and change the state:
const [people, setPeople] = useState(myPeople)
// Pass this function as a prop to PersonItem
function changeNameOfPerson(id, newName) {
const peopleCopy = [...people];
for (let person in peopleCopy) {
if (person.id === id) {
person.name = newName;
}
}
setPeople(peopleCopy);
}
A good practice when using hooks is to name the function that changes the state with the prefix 'set'.
Example: [nameOfYourVar, setNameOfYourVar]
This improves the readability.
Like Mellet said, it's important to use an unique identifier on each object. you can use this id to change an specific object from your array. Source
My component is supposed to retrieve the data for courses when the component mounts. The problem that I have is that whether I use the course Id or the course title as the key, I get the following error:
index.js:1 Warning: Each child in a list should have a unique "key" prop.
I have looked through the react docs, here on Stack Overflow, and tried different ways to get it to work. The only way I can get it to partially work is by adding an index as a parameter for map. When I use this method, I run into another problem and that is, it stops after the first iteration, even though there are 10 items. How can I fix this?
Here is my code:
CoursesPage.js
import React from 'react';
import { connect } from 'react-redux';
import * as courseActions from '../../redux/actions/courseActions';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
class CoursesPage extends React.Component {
componentDidMount() {
this.props.actions.loadCourses().catch(error => {
alert("Loading courses failed" + error);
});
}
render() {
return (
<>
<h2>Courses</h2>
{this.props.courses.map((course, index) => (
<div key={course[index].title}>{course[index].title}</div>
))}
</>
);
}
}
CoursesPage.propTypes = {
courses: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
}
function mapStateToProps(state) {
return {
courses: state.courses
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(courseActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage);
My mock data:
const courses = [
{
id: 1,
title: "Securing React Apps with Auth0",
slug: "react-auth0-authentication-security",
authorId: 1,
category: "JavaScript"
},
{
id: 2,
title: "React: The Big Picture",
slug: "react-big-picture",
authorId: 1,
category: "JavaScript"
},
{
id: 3,
title: "Creating Reusable React Components",
slug: "react-creating-reusable-components",
authorId: 1,
category: "JavaScript"
},
{
id: 4,
title: "Building a JavaScript Development Environment",
slug: "javascript-development-environment",
authorId: 1,
category: "JavaScript"
},
{
id: 5,
title: "Building Applications with React and Redux",
slug: "react-redux-react-router-es6",
authorId: 1,
category: "JavaScript"
},
{
id: 6,
title: "Building Applications in React and Flux",
slug: "react-flux-building-applications",
authorId: 1,
category: "JavaScript"
},
{
id: 7,
title: "Clean Code: Writing Code for Humans",
slug: "writing-clean-code-humans",
authorId: 1,
category: "Software Practices"
},
{
id: 8,
title: "Architecture Applications for the Real World",
slug: "architecting-applications-dotnet",
authorId: 1,
category: "Software Architecture"
},
{
id: 9,
title: "Becoming an Outlier: Reprogramming the Developer Mind",
slug: "career-reboot-for-developer-mind",
authorId: 1,
category: "Career"
},
{
id: 10,
title: "Web Component Fundamentals",
slug: "web-components-shadow-dom",
authorId: 1,
category: "HTML5"
}
];
const authors = [
{ id: 1, name: "Cory House" },
{ id: 2, name: "Scott Allen" },
{ id: 3, name: "Dan Wahlin" }
];
const newCourse = {
id: null,
title: "",
authorId: null,
category: ""
};
module.exports = {
newCourse,
courses,
authors
};
Edit:
I am using Redux Thunk.
Here is my actionType.js file:
export const CREATE_COURSE = "CREATE_COURSE";
export const LOAD_COURSES_SUCCESS = "LOAD_COURSES_SUCCESS";
Here is my CourseActions.js file:
import * as types from './actionTypes';
import * as courseApi from "../../api/courseApi";
export function createCourse(course) {
return { type: types.CREATE_COURSE, course };
}
export function loadCourseSuccess(courses) {
return { type: types.LOAD_COURSES_SUCCESS, courses };
}
export function loadCourses() {
return function (dispatch) {
return courseApi.getCourses().then(courses => {
dispatch(loadCourseSuccess(courses));
}).catch(error => {
throw error;
})
}
}
Here is my courseReducer.js file:
import * as types from '../actions/actionTypes';
export default function courseReducer(state = [], action) {
switch (action.type) {
case types.CREATE_COURSE:
return [...state, { ...action.course }];
case types.LOAD_COURSES_SUCCESS:
return [...state, { ...action.courses }];
default:
return state;
}
}
Any help would be appreciated.
Thanks.
P.S. I know that you should use Id for key. But the way it has to be done for now is using the title of the course as the key.
With your edits, I think we can more effectively help you. For future, it would be beneficial to post an example of your code not working on https://codesandbox.io/
Also to help yourself out when you debug, isolate the react component from the use of redux. This will allow you to ensure your react component renders when given data, then focus on getting redux to provide you the data your react component has.
You can do this by first defining mockdata inside your react component, then moving that mock data to your reducer, then finally replacing the mock data with the live api call.
On to the code:
You have two issues: the first is that you want to index into the array courses but instead due to a typo, you are actually using the property accessor into the object course
key={course[index].title}
As your question states you must use the title as the key simply change the div to be:
<div key={course.title}>{course.title}</div> and your code should work as expected.
Once you have addressed that, then re-enable loading data from your API call using Redux, and you can address the issues with Redux.
Looking at your reducer, you have a clear bug, and depending on your use case, a potential bug:
case types.LOAD_COURSES_SUCCESS:
return [...state, { ...action.courses }];
action.courses is an array, and the code is creating a new array that contains all the elements of the previous array state and adding a new object, which contains the contents of a destructured array.courses array.
Which that does is effectively append a single object to your array, and the object is comprised of elements from your courses array. The item index becomes the key, and the item itself is the value.
You can visualize it here: https://codesandbox.io/s/divine-pond-nr3j8
Instead you want
return [...state, ...action.courses];
The second, potential bug, is that you are actually appending the results of the courses api. Which for subsequent calls to load courses, you will duplicate the data. That may be what you want, but I am going to assume that is not what you want.
So instead, your LOAD_COURSES_SUCCESS case should be rewritten to simply be:
return [...action.courses];
Since each course has a unique ID field, the problem could be solved by using the id as a key
render() {
return (
<>
<h2>Courses</h2>
{this.props.courses.map(course => (
<div key={course.id}>{course.title}</div>
))}
</>
);
}
The issue is here:
{this.props.courses.map((course, index) => (
<div key={course[index].title}>{course[index].title}</div>
))}
//course in itself is data
Solution:
{this.props.courses.map((course, index) => (
<div key={`${course.title}-${index}`}>{course.title}</div>
))}
Better way would always to take care unique id for key
Here's the code snippet for render method.
Give it a try.
*Update = use course title as key
render() {
const courseList = this.props.courses.map(course => (
<div key={course.title}></div>
));
return (
<>
<h2>Courses</h2>
<div>{courseList}</div>
</>
);
}
There are many ways you can pass the key when it is iterating...
var courses = this.state.courses.map(function(course, index) {
return(
<div key={index}>
<div key={course.title} id={course.title}>
<h2 key={"header"+course.title}>{course.title}</h2>
</div>
</div>
)
});
But you should try to pass a unique id rather than a string.
I am new to React and I am trying to navigate the JSX syntax, combined with state/ props and pure Javascript.
I am passing props from the parent component (App) to the child component (Character). One of the props is an array of numbers which I am trying to print to the page, inside a paragraph, when the child component gets rendered. I figured it out how to print all other props (which are either strings or numbers) but I can't find a solution for the seasons array. I can print all the elements of the array to the console using a forEach loop but I have no idea how to integrate the result into JSX. I would like for the last paragraph of the child component to read Seasons: 1, 2, 3 or how many numbers the array has.
EDIT: I would like for each printed season to be a separate element (a link for example) and not to transform the array into a single string which reads "1, 2, 3" (like when using join(,).
Here is my code for the App component:
import React, { Component } from 'react';
import './App.css';
import Characters from './Characters'
class App extends Component {
constructor(props) {
super(props);
this.state = {
characters: [
{
screenName: "Uhtred Uhtredson",
description: "Uthred's younger son, originally named Osbert.",
realName: "Alexander Dreymon",
seasons: [1, 2, 3],
id: 1
},
{
screenName: "Brida",
description: "Former lover of Uhtred. Ally to Ragnall Ivarsson. Lady of Dunholme and widow to Ragnar.",
realName: "Emily Cox",
seasons: [1, 2, 3],
id: 2
},
{
screenName: "King Edward",
description: "King Alfred's son and King of Wessex.",
realName: "Timothy Innes",
seasons: [1, 2, 3],
id: 3
}
]
}
}
render() {
return (
<div className="App" >
<Characters theCharacters={this.state.characters} />
</div>
);
}
}
export default App;
And this is the code for the Character component:
import React from 'react';
const Characters = (props) => {
// we use destructuring by storing the array of objects into the characters const
const { theCharacters } = props;
const charactersList = theCharacters.map(character => {
return (
<div className="character" key={character.id}>
<p>On screen name: {character.screenName}</p>
<p>Short description: {character.description}</p>
<p>Real name: {character.realName}</p>
<p>Apparitions: {
character.seasons.forEach(season => {
return season; //this here doesn't do anything
//console.log(season);
})}</p>
</div>
)
})
return (
<div className="character_list">
{charactersList}
</div>
)
}
export default Characters;
Thank you!
character.seasons.join(',') it takes array elements and returns string, comma is a separator
forEach doesn't return anything. You should use map.
{
character.seasons.map(season => <p>{season}</p>)
}
If you render an array of JSX elements, they'll render just fine; so you can just map the array of values to an array of JSX elements. Note that in these cases, it's important for each element to have a unique key that cannot change for that element, even if it moves positions in the array, to help React render them properly. Luckily, you already have an ID for each character you can use :)
So in your case, you can do something like this:
return (
<div className="character_list">
{charactersList.map(character => (
<span key={character.id}>{character.screenName}</span>
))}
</div>
)
And of course you can render more than just the screenName however you want in there.
Is there any way to force StencilJS to export enums in order to be used by regular JavaScript code?
Generated JavaScript file exports the component classes only.
export { Calendar, CalendarDailyView, CalendarMonthlyView, CalendarWeeklyView, CalendarYearlyView };
It does not export other classes or Enums defined using JavaScript.
let's say an Enum is defined in TypeScript as follows:
export enum ViewType {
daily = 0,
weekly = 1,
monthly = 2,
yearly = 3
}
the generated JavaScript file contains:
var ViewType;
(function (ViewType) {
ViewType[ViewType["daily"] = 0] = "daily";
ViewType[ViewType["weekly"] = 1] = "weekly";
ViewType[ViewType["monthly"] = 2] = "monthly";
ViewType[ViewType["yearly"] = 3] = "yearly";
})(ViewType || (ViewType = {}));
how to force StencilJS to add ViewType to the exported types list?
Enum is entirely a TypeScript concept. JavaScript has no concept of Enums, and the generated code you see is the 'best' solution that TypeScript can generate, given all of the functionality TypeScript Enums have.
To make it compatible with plain old JavaScript it might be worth declaring ViewType as a basic array, e.g.
export const ViewType = [
{ id: 1, name: 'Day' },
{ id: 2, name: 'Month' },
{ id: 3, name: 'Year' }
];
Much easier to interpret in JS than the compiled TypeScript, and a better solution if you're going to be rendering any of these data to the screen.
To make this answer Stencily, here's my example in use:
list-items.ts
export const ListItems = [
{ id: 1, name: 'Day' },
{ id: 2, name: 'Month' },
{ id: 3, name: 'Year' }
];
my-dropdown.tsx
import { Component, State } from '#stencil/core';
import { ListItems } from './list-items';
#Component({
tag: 'my-dropdown'
})
export class MyDropdown {
#State() state: { selectedId?: number } = {};
private onSelectedValueChanged(evt: UIEvent) {
this.state = { ...this.state, selectedId: parseInt((evt.target as HTMLInputElement).value) };
}
render() {
return (
<div>
<select onChange={this.onSelectedValueChanged.bind(this)}>
<option>Please select...</option>
{ListItems.map(i => <option value={i.id}>{i.name}</option>)}
</select>
<br />Selected ID: {this.state.selectedId}
</div>
);
}
}
Important: To reuse your exported collection or enum in other applications, be sure to build as dist (see outputTargets documentation in your stencil.config.js.
In this example, building as dist will include the separate export in /dist/collection/my-dropdown/list-items.js so that it can be re-used in other dependent apps and libraries.