I am trying to get my head around props and how they work exactly. Here is my layout so far.
I have created a page called "TodoData.js" which has all of my Todos
const todoss = [
{
id: 1,
text: "First Todo"
},
{
id: 2,
text: "Second Todo"
},
{
id: 3,
text: "Third Todo"
},
{
id: 4,
text: "Fourth Todo"
}
]
export default todoss;
I then have my main page "Todolist.js", I have imported the data with "import TodoData from './TodoData'" at the top but I can't figure out exactly how to take that data and map it out onto the page, how would i do this?
You can use map() function to iterate an array.
import TodoData from './TodoData'
render() {
return (
<div>
{TodoData.map(function(data, idx){
return (<li key={idx}>{data.id}:{data.text}</li>)
})}
</div>
);
}
This is the output:
1:First Todo
2:Second Todo
3:Third Todo
4:Fourth Todo
You can use any styling you need.
Saving data internally as state is the "React" way of handling data.
In a real world application this data is going to come from an external source and if the developer doesn't know how to save data internally he will have no idea what to do.
components-and-props
state
Don't import the data, save it in the state of your Todos component and pass it as props to Todolist.
// this will act as a presentation of our data
const TodosList = ({ todos }) => (
<ul>
{todos.map(({ id, text }) => (
<li key={id}>{text}</li>
))}
</ul>
);
// This will act as a container for our data
class Todos extends React.Component {
state = {
todos: [
{
id: 1,
text: "First Todo"
},
{
id: 2,
text: "Second Todo"
},
{
id: 3,
text: "Third Todo"
},
{
id: 4,
text: "Fourth Todo"
}
]
};
render() {
return <TodosList todos={this.state.todos} />;
}
}
Related
I am new to react, I am programming the function onAdd(), when I call that it should add the item to state and then I update the hook with the new item setBooks(state).
const state = {
books: [
{ id: 0, rating: 4, title: "Harry Potter y el cáliz de fuego", image: "libro01.jpg"},
{ id: 1, rating: 3, title: "The shining", image: "libro02.jpg" },
{ id: 2, rating: 5, title: "Código Da Vinci", image: "libro03.jpg" },
{ id: 3, rating: 5, title: "El principito", image: "libro04.jpg" },
{ id: 4, rating: 5, title: "Sobrenatural", image: "libro05.jpg" },
],
copyBooks: []
};
function App() {
const [books, setBooks] = useState(state);
const onAdd = (item)=>{
//add new item to books
console.log('Add: ', item);
const id = state.books[state.books.length-1].id++;
item['id'] = id;
state.books.push(item);
setBooks(state);
}
return (
<div className="App">
<Menu onadd={onAdd}/>
<List items={books.books}/>
</div>
);
}
this works fine internally, I print the books object and it is up to date.
When I try to print the data of the child <List /> component is not printed, until I make a change on the server and it is updated.
I came to the conclusion that the problem is that the List component <List /> is not refreshed.
books refers to hooks and books.books to the data.
I do not know how I can solve it, I know it is something basic but I am starting in this technology.
Your onAdd function is mutating state, meaning React sees the same object reference as before and will not update. To make sure an update happens, copy the array and set the state to the new copy:
const onAdd = (item) => {
//add new item to books
console.log('Add: ', item);
const id = state.books[state.books.length-1].id + 1;
item['id'] = id;
const newBooks = [...state.books, item];
setBooks({ ...state, books: newBooks });
}
Edit: by the way, I might recommend some less confusing terminology when naming variables. The local books variable actually refers to the entire state and then books.books refers to the actual books... that's going to cause mistakes because it's very confusing.
For some reason my React component seems to remember its old state when going to another tab and back again, instead of reloading completely.
Basically when I click on the "Create" tab in my navbar and back to the "Board" tab data is populated twice instead of once, see image below. When going back the Board component this.state has two of each taskIds, as if it the component state still had the data from the initial page load when loading again. I have a React component looking like this:
const columnOrder = ['todo', 'in-progress', 'in-review', 'done']
const EMPTY_COLUMNS = {
'todo': {
id: 'todo',
title: 'TODO',
taskIds: []
},
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
'in-review': {
id: 'in-review',
title: 'In Review',
taskIds: []
},
'done': {
id: 'done',
title: 'Done',
taskIds: []
}
};
export class Board extends Component {
constructor(props) {
super(props);
this.onLoadEpic = this.onLoadEpic.bind(this);
this.state = {
columnOrder: columnOrder,
columns: {
'in-progress': {
id: 'in-progress',
title: 'In Progress',
taskIds: [],
},
// ...more columns similar to above
},
};
// Load state data on mount
componentDidMount() {
loadEpic(arg1, arg2);
}
// Async function loading items from DB and formatting into useful columns
async loadEpic(arg1, arg2) {
axios.get(...)
.then((response) => {
let data = response.data;
let newTasks = {};
let newColumns = EMPTY_COLUMNS;
data.taskItems.forEach(function(item) {
let id = item.id.toString();
newColumns[item.status]["taskIds"].push(id);
newTasks[id] = {
...item,
id: id
}
});
this.setState({
tasks: newTasks,
columns: newColumns
});
})
}
render() {
// Prints ["7"] on initial load and ["7", "7"] after going back and forth
console.log(this.state.columns["in-progress"].taskIds);
return (
// Simplified, but this is the main idea
<Container>
<DragDropContext onDragEnd={this.onDragEnd}>
{
this.state.columnOrder.map((columnId) => {
const column = this.state.columns[columnId]
const tasks = column.taskIds.map(taskId => this.state.tasks[taskId]
return (
<Column key={column.id} column={column} tasks={tasks}/>
)
}
}
</DragDropContext>
</Container>
)
}
}
and an App.js with Routing looking like this:
export default class App extends Component {
static displayName = App.name;
render () {
return (
<Layout>
<Route exact path='/' component={Board} />
<Route exact path='/create' component={Create} />
</Layout>
);
}
}
Okay, so I figured it out: it's the EMPTY_COLUMNS constant that is bugging out. When the component is re-rendered, the same EMPTY_COLUMNS object is referenced - so the constant is being appended to. Instead, I should make a copy of the empty columns:
// Before - same object is being appended to, doesn't work
let newColumns = EMPTY_COLUMNS;
// After - create a deep copy of the constant, does work
let newColumns = JSON.parse(JSON.stringify(EMPTY_COLUMNS));
I have a class component that Renders a list of elements and I need to focus them when an event occurs.
Here is an example code
class page extends React.Component {
state = {
items: [array of objects]
}
renderList = () => {
return this.state.items.map(i => <button>{i.somekey}</button>)
}
focusElement = (someitem) => {
//Focus some item rendered by renderList()
}
render(){
return(
<div>
{this.renderList()}
<button onClick={() => focusElement(thatElement)}>
</div>
)
}
}
I know that I need to use refs but I tried several ways to do that and I couldn't set those refs properly.
Can someone help me?
you should use the createRefmethod of each button that you would like to focus, also you have to pass this ref to the focusElement method that you have created:
const myList = [
{ id: 0, label: "label0" },
{ id: 1, label: "label1" },
{ id: 2, label: "label2" },
{ id: 3, label: "label3" },
{ id: 4, label: "label4" },
{ id: 5, label: "label5" }
];
export default class App extends React.Component {
state = {
items: myList,
//This is the list of refs that will help you pick any item that ou want to focus
myButtonsRef: myList.map(i => React.createRef(i.label))
};
// Here you create a ref for each button
renderList = () => {
return this.state.items.map(i => (
<button key={i.id} ref={this.state.myButtonsRef[i.id]}>
{i.label}
</button>
));
};
//Here you pass the ref as an argument and just focus it
focusElement = item => {
item.current.focus();
};
render() {
return (
<div>
{this.renderList()}
<button
onClick={() => {
//Here you are able to focus any item that you want based on the ref in the state
this.focusElement(this.state.myButtonsRef[0]);
}}
>
Focus the item 0
</button>
</div>
);
}
}
Here is a sandbox if you want to play with the code
I'm still getting to grips with react but I can't see why this isn't working, it should be passing the props from tabs into <Tab /> and outputting the button each time.
If I put no text next to {this.props.content} it doesn't display anything, if I put testText next to {this.props.content} it will output the button 5 times but only display testText not the name field it should be displaying via the content={item.name} prop
class TopCategories extends React.Component {
render() {
const Tab = () => (
<TestBtn key={this.props.key} >
testText {this.props.content}
</TestBtn>
)
const items = [
{ id: 1, name: 'tab-1', text: 'text' },
{ id: 2, name: 'tab-2', text: 'text' },
{ id: 3, name: 'tab-3', text: 'text' },
{ id: 4, name: 'tab-4', text: 'text' },
{ id: 5, name: 'tab-5', text: 'text' },
]
const tabs = items.map(item =>
<Tab key={item.id} content={item.name} />,
)
return (
<Container>
<Wrapper>
{tabs}
</Wrapper>
</Container>
)
}
}
export default TopCategories
You need to pass props to the stateless function and since it's a stateless component, this is not available. It should be something like:
const Tab = (props) => {
return (
<TestBtn key={props.key} >
testText {props.content}
</TestBtn>
);
}
I need to append this data response example in my React app.
DATA Response
[
{
"trackInfo": {
"id": 1,
"title": "Guns & Dogs",
"artist": "Portugal, The Man",
"album": "The Satanic Satanist"
},
"trackUrl": "https://s3-us-west-2.amazonaws.com/teddarcuri.monarch/Portugal.+The+Man+-+Guns+%26+Dogs+-+The+Satanic+Satanist.mp3",
"albumArt": "http://ecx.images-amazon.com/images/I/61X7CiBpZ6L.jpg"
}
]
React JS
class App extends React.Component {
constructor(props){
super(props)
this.state = { //initial empty details
details : {}
}
}
componentDidMount(){
//place the ajax call where ever you need
$.ajax() //call ajax
.done((data) => {
this.setState({ //this setState will re render the UI with the new state
details: { //change the key value pairs as needed
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
}
})
})
}
render() {
if(!this.state.details.id) return false //renders nothing when no details available
return (
<div id="app">
<MusicPlayer
id={this.state.details.id}
visualizerType="RIPPLES"
theme={darkTheme}
trackInfo={this.state.details.trackInfo}
trackUrl={this.state.details.trackUrl}
albumArt={this.state.details.albumArt}
utilities={true}>
</MusicPlayer>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById("app")
);
Full code example here
Working example with preloaded data here
So my question is, how can I append the new data in React using Ajax?
A code example will be really appreciate, thanks.
I think you want to show a list of MusicPlayer, so I changed your code:
[you need to read more about state in react]
class App extends React.Component {
constructor(props){
super(props)
this.state = { //initial empty details
details : [] // use array
}
}
componentDidMount(){
//place the ajax call where ever you need
$.ajax() //call ajax
.done((data) => {
let array = this.state.details;
array = [...array, {
id: data.id,
trackInfo: {
title: data.title,
artist: data.artist,
album: data.album,
},
trackUrl: data.trackUrl,
albumArt: data.albumArt,
}];
this.setState({
details: array
})
})
}
render() {
if(!this.state.details.id) return false //renders nothing when no details available
return (
<div id="app">
{
this.state.details.map((detail) => {
return <MusicPlayer
id={detail.id}
visualizerType="RIPPLES"
theme={darkTheme}
trackInfo={detail.trackInfo}
trackUrl={detail.trackUrl}
albumArt={detail.albumArt}
utilities={true}>
</MusicPlayer>
});
}
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById("app")
);