I have the following code. It doesn't render the fullRecipe items at all but i see nothing wrong in here. I have trouble learning this framework and after asking nobody knows what's happening... Do you see what's wrong?
Thanks
class Index extends React.Component {
constructor() {
super();
}
render() {
var list_results = this.props.recipes.map(function(recipe, index){
console.log(index); //Happens
console.log(recipe); //Happens
return (<fullRecipe recipe={recipe}></fullRecipe>);
});
return (<ul>{this.list_results}</ul>)
}
}
function fullRecipe(props) {
console.log(props || "no props"); // Doesnt happen
return (<li><div class="delete-button">Delete</div>{props.name} - {props.ingredients}</li>);
}
fullRecipe needs to either be part of the Index class or made into another component.
You're also using this.list_results, which should be just list_results. this is the context of the whole class, whereas your var is local to render().
The simplest method would be:
class Index extends React.Component {
constructor() {
super();
}
fullRecipe() {
return (<li><div class="delete-button">Delete</div>{this.props.name} - {this.props.ingredients}</li>);
}
render() {
var list_results = this.props.recipes.map((recipe, index) => this.fullRecipe(recipe));
return (<ul>{list_results}</ul>)
}
}
EDIT
I'm not sure what I was thinking with the above. Really, it should be two components, and neither one needs to be stateful.
//Index.js
export default const Index = ({ recipes }) => {
return (
<ul>
{
recipes.map( ({ ingredients, name }, index) => {
return <Recipe key={index} ingredients={ingredients} name={name} />
})
}
</ul>
);
}
//Recipe.js
export default const Recipe = ({ ingredients, name }) => {
return (
<li>
<button className="delete-button">Delete</button>
{name} - {ingredients}
</li>
);
}
Incorrect use of function/component
You can either create a Component called fullRecipe to display the information, or bring the function fullRecipe to Index component.
check this link https://facebook.github.io/react/docs/multiple-components.html
Related
I'm building a new project with React. I have a component that defines several child components like this:
class TimeStepDisplayGraph extends React.Component {
render () {
return <div id={ this.props.id }>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
<TimeStepDisplayGraphNode class="graphNodeStyles"/>
</div>;
}
}
Now ideally what I would like to do is not have the number of nodes created defined explicitly, but rather through a call like:
function createGraphNode() {
return React.createElement( <TimeStepDisplayGraphNode class="graphNodeStyles"/>);
}
That I call x number of times. It seems like something really simple to do, and I'm sure I'll be kicking myself later for being so stupid, but at the moment I'm really at a loss for how to do this. Any help would be appreciated.
There are many ways to do this. The simplest would be to simply create an array of size n and map over it, returning a React component for each one:
render () {
const arr = new Array(6);
return (
<div id={ this.props.id }>
{arr.map((ea, i) => {
return <TimeStepDisplayGraphNode key={i} class="graphNodeStyles"/>
}}
</div>
);
}
Note that you need to add the key prop to each created node to uniquely identify it between renders. Using i is normally not ideal (because it does not actually uniquely identify which node is which) but in the absence of any other identifying data it will do.
This is a very common pattern in React - see the official docs for more info.
You can just put it in a function, like you are thinking, but you don't need the React.createElement. Something like this would suffice:
class TimeStepDisplayGraph extends React.Component {
...
render () {
return (
<div id={ this.props.id }>
{createGraphNode()}
</div>
)
}
}
...
}
function createGraphNode() {
return <TimeStepDisplayGraphNode class="graphNodeStyles"/>;
}
Or to add it n times, something like:
class TimeStepDisplayGraph extends React.Component {
...
render () {
return (
<div id={ this.props.id }>
{Array.from({ length: n }, createGraphNode)}
</div>
)
}
}
...
}
function createGraphNode(_, index) {
return <TimeStepDisplayGraphNode key={index} class="graphNodeStyles"/>;
}
Is that what you had in mind?
Yep, Muhammad is right. This is how to use a loop to do what you're asking:
class outerComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
numberOfTimestamps: 100000 // (just kidding, make that smaller!)
}
}
render () {
let myTimestamps = []
for (let i=0; i < this.state.numberOfTimestamps; i++) {
myTimestamps.push(<TimeStepDisplayGraphNode class="graphNodeStyles"/>)
}
return (
<div>
{myTimestamps}
</div>
)
}
}
Edit: Changed to for loop rather than forEach. Cannot forEach a number!
I'm building out a simple drum machine application using ReactJS and could use some help understanding how to loop through all instances of a component while outputting each instance's state.
The application UI shows 16 columns of buttons, each containing 4 unique drum rows. There is a "SixteenthNote.js" component which is essentially on column containing each "Drum.js" instance. In the "DrumMachine.js" module, I am outputting "SixteenthNote.js" 16 times to display one full measure of music. When you click on a drum button, that drum's value is pushed into the SixteenthNote' state array. This is all working as intended.
The last part of this is to create a "Play.js" component which, when clicked, will loop through all of the SixteenthNote instances and output each instance's state.
Here is the "DrumMachine.js" module
class DrumMachine extends Component {
constructor(props) {
super(props);
this.buildKit = this.buildColumns.bind(this);
this.buildLabels = this.buildLabels.bind(this);
this.buildAudio = this.buildAudio.bind(this);
this.state = {
placeArray: Array(16).fill(),
drumOptions: [
{type: 'crash', file: crash, title: 'Crash'},
{type: 'kick', file: kick, title: 'Kick'},
{type: 'snare', file: snare, title: 'Snare'},
{type: 'snare-2', file: snare2, title: 'Snare'}
]
}
}
buildLabels() {
const labelList = this.state.drumOptions.map((sound, index) => {
return <SoundLabel title={sound.title} className="drum__label" key={index} />
})
return labelList;
}
buildColumns() {
const buttonList = this.state.placeArray.map((object, index) => {
return <SixteenthNote columnClassName="drum__column" key={index} drumOptions={this.state.drumOptions}/>
});
return buttonList;
}
buildAudio() {
const audioList = this.state.drumOptions.map((audio, index) => {
return <Audio source={audio.file} drum={audio.type} key={index}/>
})
return audioList;
}
render() {
return (
<div>
<div className={this.props.className}>
<div className="label-wrapper">
{this.buildLabels()}
</div>
<div className="drum-wrapper">
{this.buildColumns()}
</div>
</div>
<div className="audio-wrapper">
{this.buildAudio()}
</div>
</div>
)
}
}
Here is "SixteenthNote.js" module
class SixteenthNote extends Component {
constructor(props) {
super(props);
this.buildColumn= this.buildColumn.bind(this);
this.buildDrumOptions = this.buildDrumOptions.bind(this);
this.updateActiveDrumsArray = this.updateActiveDrumsArray.bind(this);
this.state = {
activeDrums: []
}
}
buildDrumOptions() {
return this.props.drumOptions;
}
updateActiveDrumsArray(type) {
let array = this.state.activeDrums;
array.push(type);
this.setState({activeDrums: array});
}
buildColumn() {
const placeArray = this.buildDrumOptions().map((button, index) => {
return <Drum buttonClassName="drum__button" audioClassName="drum__audio" type={button.type} file={button.file} key={index} onClick={() => this.updateActiveDrumsArray(button.type)}/>
})
return placeArray;
}
render() {
return (
<div className={this.props.columnClassName}>
{this.buildColumn()}
</div>
)
}
}
Here is the "Drum.js" module
class Drum extends Component {
constructor(props) {
super(props);
this.clickFunction = this.clickFunction.bind(this);
this.state = {
clicked: false
}
}
drumHit(e) {
document.querySelector(`.audio[data-drum=${this.props.type}]`).play();
this.setState({clicked:true});
}
clickFunction(e) {
this.state.clicked === false ? this.drumHit(e) : this.setState({clicked:false})
}
render() {
const drumType = this.props.type;
const drumFile = this.props.file;
const buttonClasses = `${this.props.buttonClassName} drum-clicked--${this.state.clicked}`
return (
<div onClick={this.props.onClick}>
<button className={buttonClasses} data-type={drumType} onClick={this.clickFunction}></button>
</div>
)
}
}
You will need to contain the information about the activeDrums in your DrumMachine component.
That means:
In your DrumMachine component you create the state activeDrums like you have in your SixteenthNote.js. You will need to put your updateActiveDrumsArray function to your drumMachine component as well.
Then you pass this function to your SixteenthNote component like:
<SixteenthNote columnClassName="drum__column" key={index} drumOptions={this.state.drumOptions} onDrumsClick={this.updateActiveDrumsArray} />
After doing so, you can access that function via props. So, in your SixteenthNote component it should look like:
<Drum buttonClassName="drum__button" audioClassName="drum__audio" type={button.type} file={button.file} key={index} onClick={() => this.props.onDrumsClick(button.type)}/>
(Don't forget to get rid of the unneccessary code.)
With this, you have your activeDrums state in DrumMachine containing all the active drums. This state you can then send to your play component and do the play action there.
import React, {Component} from 'react';
import "./DisplayCard.css";
class DisplayCard extends Component {
runArray = (array) => {
for (var i = 0; i<array.length; i++) {
return <div>{array[i].task}</div>
}
}
renderElements = (savedTasks) =>{
if (savedTasks.length === 0) {
return <div className="noTasks"> <p>You have no saved tasks.</p> </div>
} else {
return this.runArray(savedTasks)
}
}
render() {
return (
<div className="DisplayCardContainer">
{this.renderElements(this.props.saved)}
</div>
)
}
}
export default DisplayCard;
Hey guys,
I am new to react, so this is my child component that takes state from its parent component. My goal is to re-render component every time the array this.props.saved is changed.
This component renders: <p>You have no saved tasks.</p> when the this.props.saved.length === 0 and it renders <div>{array[0].task}</div> when i enter the first task, but it keeps it at <div>{array[0].task}</div> after that. I do see that the state keeps changing and this.props.saved keeps getting bigger, but my component doesn't change anymore.
Here's your problem:
runArray = (array) => {
for (var i = 0; i<array.length; i++) {
//the first time we get here, it immediately ends the function!
return <div>{array[i].task}</div>
}
}
This loop only ever goes through once (at i=0) and then returns, exiting the runArray function and cancelling the rest of the loop. You probably wanted to return an array of elements, one for each of the tasks. I recommend using Array.map() for this, which takes an array and transforms each element, creating a new array:
runArray = (array) => {
return array.map(arrayElement => <div>arrayElement.task</div>);
}
This should do the trick. Note that React may complain about the fact that your elements lack the key property - see the documentation for more info: https://reactjs.org/docs/lists-and-keys.html
The problem is in your runArray function. Inside your loop, you are returning the first element and that's it. My guess is, you see only the first entry?
When you are trying to render all your tasks, I would suggest to map your tasks, e.g.
runArray = (array) => array.map(entry => <div>{entry.task}</div>)
It is because you write wrong the runArray function. You make a return in the for loop so it breaks after the first iteration. It will not iterate over the full array.
You need to transform your for loop to a map : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
runArray = (array) => {
return array.map(v => <div>{v.task}</div>)
}
Does it fix your issue ?
You have to update state of the component to trigger render function. Your render function is not triggered because you did not update the state when the props changed. There are many ways to update state when props updated. One method may be the following:
componentWillReceiveProps(nextProps){
if (nextProps.saved !== this.props.saved) {
this.setState({ saved: nextProps.saved })
}
}
Also change yoour render function to use state of the component as below:
renderElements = () =>{
if (this.state.savedTasks.length === 0) {
return <div className="noTasks"> <p>You have no saved tasks.</p> </div>
} else {
return this.runArray(this.state.savedTasks)
}
}
Use .map so that it renders your task correctly. You can remove runArray and rely entirely on props so you don't need to pass arguments across functions as it can get messy quickly. Here's a quick running example of how to create a parent component where you can add a task and pass them into a component so that it renders your data when props are changed, therefore making it reactive.
class App extends React.Component {
state = {
taskLabel: "",
tasks: [
{
id: 1,
label: "Do something"
},
{
id: 2,
label: "Learn sometihng"
}
]
};
handleInput = evt => {
this.setState({
[evt.target.name]: evt.target.value
});
};
handleSubmit = evt => {
evt.preventDefault();
this.setState(prevState => ({
taskLabel: "",
tasks: [
...prevState.tasks,
{
id: prevState.tasks.length + 1,
label: this.state.taskLabel
}
]
}));
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
name="taskLabel"
type="text"
placeholder="Task label"
value={this.state.taskLabel}
onChange={this.handleInput}
/>
<button>Create task</button>
</form>
<DisplayCard tasks={this.state.tasks} />
</div>
);
}
}
class DisplayCard extends React.Component {
renderTasks = () => {
if (this.props.tasks.length !== 0) {
return this.props.tasks.map(task => (
<div key={task.id}>{task.label}</div>
));
} else {
return <div>No tasks</div>;
}
};
render() {
return <div>{this.renderTasks()}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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"></div>
I am trying to simply map over some data returned from an api and create a stateless component for each object returned. I want to be able to click on any of the components to toggle visibility of the rest of its data.
I have tried numerous ways to do it and keep hitting a brick wall, i've also scoured stack overflow and cannot seem to find an answer.
I have gotten it working by making them individual class components, however it seems like a lot of unnecessary code for just a toggle functionality.
Thank you in advance for any help or insight, here is a quick breakdown of what I have currently.
For clarification this is a simple app for me to learn about using react and an external api, it is not using redux.
fetched users in state of class component
class PersonList extends Component {
constructor(props) {
super(props);
this.state = {
resource: []
};
}
async componentDidMount() {
let fetchedData = await API_Call("people");
this.setState({ resource: fetchedData.results });
while (fetchedData.next) {
let req = await fetch(fetchedData.next);
fetchedData = await req.json();
this.setState({
resource: [...this.state.resource, ...fetchedData.results]
});
}
}
}
Then map over the results and render a component for each result
render() {
const mappedPeople = this.state.resource.map((person, i) => (
<Person key={i} {...person} />
));
return <div>{mappedPeople}</div>;
}
Is there i can make each person component a stateless component with the ability to click on it and display the rest of the data? Here is what I have currently.
class Person extends Component {
constructor(props) {
super(props);
this.state = {
visibility: false
};
}
toggleVisible = () => {
this.setState(prevState => ({
visibility: !prevState.visibility
}));
};
render() {
return (
<div>
<h1 onClick={this.toggleVisible}>{this.props.name}</h1>
{this.state.visibility && (
<div>
<p>{this.props.height}</p>
</div>
)}
</div>
);
}
}
Again thanks in advance for any insight or help!
You could keep an object visible in your parent component that will have keys representing a person index and a value saying if the person is visible or not. This way you can toggle the person's index in this single object instead of having stateful child components.
Example
class PersonList extends Component {
constructor(props) {
super(props);
this.state = {
resource: [],
visible: {}
};
}
// ...
toggleVisibility = index => {
this.setState(previousState => {
const visible = { ...previousState.visibile };
visible[index] = !visible[index];
return { visible };
});
};
render() {
const mappedPeople = this.state.resource.map((person, i) => (
<Person
key={i}
{...person}
visible={this.state.visible[i]}
onClick={() => this.toggleVisibility(i)}
/>
));
return <div>{mappedPeople}</div>;
}
}
const Person = (props) => (
<div>
<h1 onClick={props.onClick}>{props.name}</h1>
{props.visible && (
<div>
<p>{props.height}</p>
</div>
)}
</div>
);
Similar idea with #Tholle but a different approach. Assuming there is an id in the person object we are changing visibles state and toggling ids.
class PersonList extends React.Component {
constructor(props) {
super(props)
this.state = {
resource: this.props.persons,
visibles: {},
}
}
toggleVisible = id => this.setState( prevState => ({
visibles: { ...prevState.visibles, [id]: !prevState.visibles[id] },
}))
render() {
const mappedPeople =
this.state.resource.map((person, i) =>
<Person
key={person.id}
visibles={this.state.visibles}
toggleVisible={this.toggleVisible}
{...person}
/>
)
return (
<div>
{mappedPeople}
</div>
)
}
}
const Person = (props) => {
const handleVisible = () =>
props.toggleVisible( props.id );
return (
<div>
<h1 onClick={handleVisible}>
{props.name}</h1>
{props.visibles[props.id] &&
<div>
<p>{props.height}</p>
</div>
}
</div>
);
}
const persons = [
{ id: 1, name: "foo", height: 10 },
{ id: 2, name: "bar", height: 20 },
{ id: 3, name: "baz", height: 30 },
]
const rootElement = document.getElementById("root");
ReactDOM.render(<PersonList persons={persons} />, rootElement);
<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>
You can make sure your "this.state.resource" array has a visibility flag on each object:
this.state.resource = [
{ ..., visibility: true },
{ ..., visibility: false}
...
];
Do this by modifying your fetch a little bit.
let fetchedData = await API_Call("people");
this.setState({
resource: fetchedData.results.map(p => ({...p, visiblity: true}))
});
Merge your Person component back into PersonList (like you are trying to do), and on your onclick, do this:
onClick={() => this.toggleVisible(i)}
Change toggleVisible() function to do the following.
toggleVisible = (idx) => {
const personList = this.state.resource;
personList[idx].visibility = !personList[idx].visibility;
this.setState({ resource: personList });
}
So now, when you are doing:
this.state.resource.map((person, i) => ...
... you have access to "person.visibility" and your onclick will toggle the particular index that is clicked.
I think that directly answers your question, however...
I would continue with breaking out Person into it's own component, it really is good practice!
Other than better organization, one of the main reason is to avoid lamdas in props (which i actually did above). Since you need to do an onClick per index, you either need to use data attributes, or actually use React.Component for each person item.
You can research this a bit here:
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
BTW you can still create "components" that aren't "React.Component"s like this:
import React from 'react';
const Person = ({ exProp1, exProp2, exProp3}) => {
return <div>{exProp1 + exProp2 + exProp3}</div>
}
Person.propTypes = {
...
}
export default Person;
As you can see, nothing is inheriting from React.Component, so you are getting the best of both worlds (create components without creating "Components"). I would lean towards this approach, vs putting everything inline. But if your application is not extremely large and you just want to get it done, going with the first approach isn't terribly bad.
(Pardon the verbose question. I'm brand new to React and ES6, and I'm probably overly-convoluting this.)
I am writing an app that contains a button component. This button calls a method onAddChild that creates another component of class ColorModule by adding a value to an array stored in the App's state.
In each newly created ColorModule, I want to include another button that will remove the module. Since this component is created by an array.map method, my thought is that if I can find the index of the array item that corresponds with the component and use that index in array.splice then perhaps that component will be removed (untested theory). That said, I'm not really sure how to find the index where I would use this in my onRemoveModule method.
Two part question: 1) How would I go about finding the index of the array item in my state, and 2) if I'm completely off base or there's a better way to do this altogether, what does that solution look like?
imports...
class App extends Component {
static propTypes = {
children: PropTypes.node,
};
constructor(props) {
super(props);
this.state = {
// Here's the array in question...
moduleList: [1],
};
this.onAddChild = this.onAddChild.bind(this);
this.onRemoveModule = this.onRemoveModule.bind(this);
this.className = bemClassName.bind(null, this.constructor.name);
}
onAddChild(module) {
const moduleList = this.state.moduleList;
this.setState({ moduleList: moduleList.concat(1) });
}
onRemoveModule( e ) {
e.preventDefault();
...¯\_(ツ)_/¯
}
render() {
const { className } = this;
return (
<div className={className('container')}>
<Header onAddChild={this.onAddChild} /> /* Add module button lives here */
<div className="cf">
{this.state.moduleList.map(
( delta, index ) => {
return (
<ColorModule
className="cf"
onRemove={this.onRemoveModule}
key={index}
moduleId={'colorModule' + index}
/>
); /* Remove module button would live in the module itself */
}
)}
</div>
</div>
);
}
}
export default App;
Well this part is pretty easy, all you need to do is pass the index as prop to the ColorModule component and when calling the onRemove method in it you could pass it back to the onRemoveModule. However react optimizes based on keys and its a really good idea to have a unique id given to each module instance.
class App extends Component {
static propTypes = {
children: PropTypes.node,
};
constructor(props) {
super(props);
this.state = {
// Here's the array in question...
moduleList: [1],
};
this.onAddChild = this.onAddChild.bind(this);
this.onRemoveModule = this.onRemoveModule.bind(this);
this.className = bemClassName.bind(null, this.constructor.name);
}
onAddChild(module) {
const moduleList = this.state.moduleList;
this.setState({ moduleList: moduleList.concat(uuid()) }); //uuid must return a unique id everytime to be used as component key
}
onRemoveModule( index ) {
// now with this index you can update the moduleList
}
render() {
const { className } = this;
return (
<div className="cf">
{this.state.moduleList.map(
( delta, index ) => {
return (
<ColorModule
className="cf"
index={index}
onRemove={this.onRemoveModule}
key={delta}
moduleId={'colorModule' + delta}
/>
);
}
)}
</div>
);
}
}
Now in ColorModule component
class ColorModule extends React.Component {
onRemoveClick=() => {
this.props.onRemove(this.props.index);
}
}
Check this answer for more details on how to pass data from Child component to Parent
I ended up solving this problem using some of the guidance here from #ShubhamKhatri (didn't know about unique ID generation!), but I took a slightly different approach and handled the solution using state manipulation in App without needing a new method in my ColorModule component. I also never knew about currying in ES6, so that discovery made passing in the index values needed to manipulate my state array possible
If I'm off-base here or being inefficient, I'm definitely still open to feedback on a better way!
class App extends Component {
constructor(props) {
super(props);
this.state = {
moduleList: [{ id: UniqId(), removeModule: false }],
};
this.onAddChild = this.onAddChild.bind(this);
this.className = bemClassName.bind(null, this.constructor.name);
}
onAddChild(module) {
const moduleList = this.state.moduleList;
this.setState({
moduleList: moduleList.concat({
id: UniqId(),
removeModule: false,
}),
});
}
onRemoveModule = ( i, arr ) => (e) => {
const moduleList = this.state.moduleList;
e.preventDefault();
moduleList[i].removeModule = true;
this.setState({ moduleList: moduleList });
}
render() {
const { className } = this;
return (
<div className={className('container')}>
<Header onAddChild={this.onAddChild} />
<div className="cf">
{this.state.moduleList.map(
( delta, index ) => {
if ( !this.state.moduleList[index].removeModule ) {
return (
<ColorModule
className="cf"
onRemove={this.onRemoveModule( index, this.state.moduleList )}
index={index}
key={delta.id}
moduleId={'colorModule' + delta}
/>
);
}
}
)}
</div>
</div>
);
}
}