Apologies if the question is confusing. Basically I have this html code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A Gentle Introduction</title>
<script
src="https://rawgit.com/flatiron/director/master/build/director.min.js">
</script>
<script>
var author = function () { console.log("author"); };
var books = function () { console.log("books"); };
var viewBook = function (bookId) {
console.log("viewBook: bookId is populated: " + bookId);
};
var routes = {
'/author': author,
'/books': [books, function() {
console.log("An inline route handler.");
}],
'/books/view/:bookId': viewBook
};
var router = Router(routes);
router.init();
</script>
</head>
<body>
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
</ul>
</body>
</html>
which is clearly in a .html file. I want to change this to a .js file so that I can put html within the js so that when the different links are clicked, what is routed/returned is different.
I dont really know how to directly put this into a javascript file and then get the router to work. This is where the html file came from https://github.com/flatiron/director#client-side-routing and I am trying to use this flatiron/director router.
Any help would be great!
I was able to make it work with react and jsx and the routing code outside react itself.
Written with es6/es2015
app.js
const author = () => { console.log("author"); };
const books = () => { console.log("books"); };
const viewBook = (bookId) => { console.log("viewBook: bookId is populated: " + bookId); };
const routes = {
'/author': author,
'/books': [books, () => { console.log("An inline route handler."); }],
'/books/view/:bookId': viewBook
};
const router = Router(routes);
router.init();
class SampleRouting extends React.Component {
render() {
return (
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
</ul>
)
}
}
React.render( <SampleRouting/> , document.getElementById('root'));
index.html
<div id="root"></div>
sample: http://s.codepen.io/oobgam/debug/vNoogO
_edited app.js to reflect the updating of state and page header
class App extends React.Component {
constructor(props) {
super(props);
this.state = { currentPage: 'author' }
}
componentDidMount() {
const author = () => { this.setState({currentPage: 'author'}) };
const books = () => { this.setState({currentPage: 'Books'}); };
const viewBook = (bookId) => { this.setState({currentPage: 'Book ' + bookId }); };
const routes = {
'/author': author,
'/books': books,
'/books/view/:bookId': viewBook
};
const router = Router(routes);
router.init();
}
render() {
return (
<div>
<h1>{ this.state.currentPage }</h1>
<SampleRouting />
</div>
);
}
}
// stateless function
const SampleRouting = () => {
return (
<ul>
<li>#/author</li>
<li>#/books</li>
<li>#/books/view/1</li>
< /ul>
)
}
Related
I am displaying a data in the list. Each item is having a Edit link. I am not sure how to do this.
Link click will open a component in another window.Edit. Please help me how to resolve this.
Please find my sandbox: https://codesandbox.io/s/withered-fog-mx7kvb
import Edit_Descr from "./Edit_Descr";
const data = `<?xml version="1.0"?>
<Category>
<description description-id="11" display-sequence="2">testing</description>
<description description-id="15" display-sequence="5">Guide</description>
<description description-id="20" display-sequence="7">test</description>
</Category>
</xml>`;
const REQUEST_URL = "";
const axios = {
get: () =>
new Promise((resolve) => {
setTimeout(resolve, 1000, { data });
})
};
export class Add_Descr extends React.Component {
constructor(props) {
super(props);
this.state = {
proddescriptions: [],
proddescription_id: ""
};
}
componentDidMount() {
axios
.get(REQUEST_URL, { "Content-Type": "application/xml; charset=utf-8" })
.then(response => {
const jsonDataFromXml = new XMLParser().parseFromString(response.data);
const descriptions = jsonDataFromXml.getElementsByTagName(
"description"
);
const proddescriptions = descriptions.map(({ attributes, value }) => ({
id: attributes["description-id"],
value
}));
this.setState({
proddescriptions
});
});
}
render() {
return (
<div><form>
{this.state.proddescriptions.map((item, index) => {
return (
<li key={item.id}>
Edit
{ReactHtmlParser(item.value)}
</li>
);
})}
</form>
</div>
);}}
export default Add_Descr;
I have a simple word/definition app in React. There is an edit box that pops up to change definition when a user clicks on "edit". The new definition provided is updated in the state when I call getGlossary(), I see the new definition in inspector and a console.log statement in my App render() function triggers too. Unfortunately, I still have to refresh the page in order for the new definition to be seen on screen. I would think that calling set state for this.state.glossary in the App would trigger a re-render down to GlossaryList and then to GlossaryItem to update it's definition but I'm not seeing it :(.
App.js
class App extends React.Component {
constructor() {
super();
this.state = {
glossary: [],
searchTerm: '',
}
this.getGlossary = this.getGlossary.bind(this); //not really necessary?
this.handleSearchChange = this.handleSearchChange.bind(this);
this.handleAddGlossaryItem = this.handleAddGlossaryItem.bind(this);
this.handleDeleteGlossaryItem = this.handleDeleteGlossaryItem.bind(this);
//this.handleUpdateGlossaryDefinition = this.handleUpdateGlossaryDefinition.bind(this);
}
getGlossary = () => {
console.log('getGlossary fired');
axios.get('/words').then((response) => {
const glossary = response.data;
console.log('1: ' + JSON.stringify(this.state.glossary));
this.setState({ glossary }, () => {
console.log('2: ' + JSON.stringify(this.state.glossary));
});
})
}
componentDidMount = () => {
//console.log('mounted')
this.getGlossary();
}
handleSearchChange = (searchTerm) => {
this.setState({ searchTerm });
}
handleAddGlossaryItem = (glossaryItemToAdd) => {
//console.log(glossaryItemToAdd);
axios.post('/words', glossaryItemToAdd).then(() => {
this.getGlossary();
});
}
handleDeleteGlossaryItem = (glossaryItemId) => {
console.log('id to delete: ' + glossaryItemId);
axios.delete('/words', {
data: { glossaryItemId },
}).then(() => {
this.getGlossary();
});
}
render() {
console.log('render app fired');
const filteredGlossary = this.state.glossary.filter((glossaryItem) => {
return glossaryItem.word.toLowerCase().includes(this.state.searchTerm.toLowerCase());
});
return (
<div>
<div className="main-grid-layout">
<div className="form-left">
<SearchBox handleSearchChange={this.handleSearchChange} />
<AddWord handleAddGlossaryItem={this.handleAddGlossaryItem} />
</div>
<GlossaryList
glossary={filteredGlossary}
handleDeleteGlossaryItem={this.handleDeleteGlossaryItem}
getGlossary={this.getGlossary}
//handleUpdateGlossaryDefinition={this.handleUpdateGlossaryDefinition}
/>
</div>
</div>
);
}
}
export default App;
GlossaryItem.jsx
import React from 'react';
import EditWord from './EditWord.jsx';
const axios = require('axios');
class GlossaryItem extends React.Component {
constructor(props) {
super(props);
this.state = {
isInEditMode: false,
}
this.glossaryItem = this.props.glossaryItem;
this.handleDeleteGlossaryItem = this.props.handleDeleteGlossaryItem;
this.handleUpdateGlossaryDefinition = this.handleUpdateGlossaryDefinition.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
}
handleUpdateGlossaryDefinition = (updateObj) => {
console.log('update object: ' + JSON.stringify(updateObj));
axios.put('/words', {
data: updateObj,
}).then(() => {
this.props.getGlossary();
}).then(() => {
this.setState({ isInEditMode: !this.state.isInEditMode });
//window.location.reload();
});
}
handleEditClick = () => {
// display edit fields
this.setState({ isInEditMode: !this.state.isInEditMode });
// pass const name = new type(arguments); data up to App to handle with db
}
render() {
return (
<div className="glossary-wrapper">
<div className="glossary-item">
<p>{this.glossaryItem.word}</p>
<p>{this.glossaryItem.definition}</p>
<a onClick={this.handleEditClick}>{!this.state.isInEditMode ? 'edit' : 'cancel'}</a>
<a onClick={() => this.handleDeleteGlossaryItem(this.glossaryItem._id)}>delete</a>
</div>
{this.state.isInEditMode ?
<EditWord
id={this.glossaryItem._id}
handleUpdateGlossaryDefinition={this.handleUpdateGlossaryDefinition}
/> : null}
</div>
);
}
}
EditWord
import React from 'react';
class EditWord extends React.Component {
constructor(props) {
super(props);
this.state = {
definition: ''
};
this.handleUpdateGlossaryDefinition = this.props.handleUpdateGlossaryDefinition;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let definition = event.target.value;
this.setState({ definition });
}
handleSubmit(event) {
//console.log(event.target[0].value);
let definition = event.target[0].value;
let update = {
'id': this.props.id,
'definition': definition,
}
//console.log(update);
this.handleUpdateGlossaryDefinition(update);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit} className="glossary-item">
<div></div>
<input type="text" name="definition" placeholder='New definition' value={this.state.definition} onChange={this.handleChange} />
<input type="submit" name="update" value="Update" />
</form>
);
}
}
export default EditWord;
Thank you
One possible way I can see to fix this is to map the data to make the id uniquely identify each list item (even in case of update). We can to do this in getGlossary() by modifying the _id to _id + definition.
getGlossary = () => {
console.log('getGlossary fired');
axios.get('/words').then((response) => {
// Map glossary to uniquely identify each list item
const glossary = response.data.map(d => {
return {
...d,
_id: d._id + d.definition,
}
});
console.log('1: ' + JSON.stringify(this.state.glossary));
this.setState({ glossary }, () => {
console.log('2: ' + JSON.stringify(this.state.glossary));
});
})
}
In the constructor of GlossaryItem I set
this.glossaryItem = this.props.glossaryItem;
because I am lazy and didn't want to have to write the word 'props' in the component. Turns out this made react loose reference somehow.
If I just remove this line of code and change all references to this.glossaryItem.xxx to this.pros.glossaryItem.xxx then it works as I expect! On another note, the line of code can be moved into the render function (instead of the constructor) and that works too, but have to make sure I'm accessing variables properly in the other functions outside render.
I'm making this music app from online video tutorial of Simplilearn but I got this one parsing error while doing this so anyone can suggest me what to do...
did I have to change the name of the class or function?
Parsing error: identifier 'App' has already been declared
import React,{ Component } from 'react';
import './App.css';
import Playlist from '';`enter code here`
import SearchBar from '';
import SearchResults from '';
import Spotify from '';
class App extends Component() {
constructor(props) {
super(props);
this.state = {
SearchResults: [],
playlistName: "new Playlist",
playlistTracks: []
};
this.search = this.search.bind(this);
this.addTrack = this.addTrack.bind(this);
this.removeTrack = this.removeTrack.bind(this);
this.updatePlaylistName = this.updatePlaylistName.bind(this);
this.savePlaylist = this.savePlaylist.bind(this);
this.removeTrackSearch = this.removeTrackSearch.bind(this);
this.doThese = this.doThese.bind(this);
}
search(term) {
Spotify.search(term).then( SearchResults => {
this.setState({ SearchResults: SearchResults });
});
}
addTrack(track) {
let tracks = this.state.playlistTracks;
if(tracks.find( savedTrack => savedTrack.id === track.id )){
return;
}
tracks.push(track);
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
let trackSearch = this.state.SearchResults;
tracks = tracks.filter( currentTrack => currentTrack.id !== track.id );
trackSearch.unshift(track);
this.setState({ playlistTracks: tracks });
}
removeTrackSearch(track) {
let tracks = this.state.SearchResults;
tracks = tracks.filter( currentTrack => currentTrack.id !== track.id );
this.setState({ SearchResults: tracks });
}
doThese(track) {
this.addTrack(track);
this.removeTrackSearch(track);
}
updatePlaylistName(name) {
this.setState({ updatePlaylistName: name });
}
savePlaylist() {
const trackUris = this.state.playlistTracks.map( track => track.uri );
Spotify.savePlaylist(this.state.playlistName, trackUris).then( () => {
this.setState({
updatePlaylistName: "new Playlist",
playlistTracks: []
});
});
}
}
function App() {
return (
<div>
<h1>
<a href = "https://localhost:3000" >Musicophile</a>
</h1>
<div className="App">
<SearchBar onSearch={this.search} />
<div className="App-playlist">
<SearchResults
SearchResults={this.state.SearchResults}
onAdd={this.doThese} />
<Playlist
playlistTracks={this.state.playlistTracks}
onNameChange={this.updatePlaylistName}
onRemove={this.removeTrack}
onSave={this.savePlaylist} />
</div>
</div>
</div>
);
}
export default App;
You are trying to initialize two variables (App in this case) which has the same name. Rename one of your components to something else.
Now the original question is solved, here's the hard part. There are so many things wrong with the code besides the duplicated App.
The class component App does not have a render function.
Mixing function components and class components. It will work but it is just bad practice.
State mutation. e.g.
let tracks = this.state.playlistTracks;
// ... omitted
tracks.push(track); // NO!!!!!
Unusable import statement. I'm not sure did you remove the import part deliberately or what, but if you copy and paste the imports directly, it will defiantly fail.
Using this in a function component.
I'm thinking on creating a React component called LoadingMask, where I can show or not (depending on the state) a loading mask from any component. The idea is showing it before an ajax call, and hiding it after I receive the data.
I don't want to display two masks at the same time, so if one component is making a request, and another one creates another request, I want to add 1 to my "MaskCounter", and substract one when the Request is finished. If the counter is 0, I need to hide the LoadingMask.
I order to do this, I think I need to create a "Singleton" component, that I can share through the whole platform, so there's only exist one LoadingMask. I also don't think it's nice to send the events to hide/show the mask to all components.
Any ideas?
To share data between components, you can :
Use a lib like Redux, and keep in shared store your mask loader status
Use the React context api from your root component, and share loader status to all childrens. See an example below :
class Application extends React.Component {
constructor() {
super();
this.state = {
nbTasks: 0
};
this.addTask = this.addTask.bind(this);
this.removeTask = this.removeTask.bind(this);
this.isLoading = this.isLoading.bind(this);
}
addTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks + 1
}));
}
removeTask() {
this.setState(prevState => ({
nbTasks: prevState.nbTasks - 1
}));
}
isLoading() {
return this.state.nbTasks > 0;
}
getChildContext() {
return {
addTask: this.addTask,
removeTask: this.removeTask,
isLoading: this.isLoading
};
}
render() {
return (
<div>
<ComponentX />
<ComponentY />
<LoadingMask />
</div>
);
}
}
Application.childContextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func,
isLoading: PropTypes.func
};
const LoadingMask = (props, context) => (
context.isLoading()
? <div>LOADING ...</div>
: null
);
LoadingMask.contextTypes = {
isLoading: PropTypes.func
};
class ComponentX extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
componentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentX ready !'
});
this.context.removeTask();
}, 3500);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentX.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
class ComponentY extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
message: 'Processing ...'
};
}
componentDidMount() {
this.context.addTask();
setTimeout(() => {
this.setState({
message: 'ComponentY ready !'
});
this.context.removeTask();
}, 6000);
}
render() {
return (
<div>
<button disabled>{this.state.message}</button>
</div>
);
}
}
ComponentY.contextTypes = {
addTask: PropTypes.func,
removeTask: PropTypes.func
};
ReactDOM.render(
<Application />,
document.getElementById('app')
);
<script src="https://unpkg.com/prop-types/prop-types.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="app"></app>
I found this library use-between to be simple, powerful and useful. It removes complexity of redux for sharing data between within functional components.
import React, { useState, useCallback } from 'react';
import { useBetween } from 'use-between';
Context/Session.ts
export const useShareableState = () => {
const [count, setCount] = useState(0);
const inc = useCallback(() => setCount(c => c + 1), []);
const dec = useCallback(() => setCount(c => c - 1), []);
return {
count,
inc,
dec
};
};
App.tsx
import { useBetween } from 'use-between';
import { useShareableState } from './src/Context/Session'
const useSharedCounter = () => useBetween(useShareableState);
const Count = () => {
const { count } = useSharedCounter();
return <p>{count}</p>;
};
const Buttons = () => {
const { inc, dec } = useSharedCounter();
return (
<>
<button onClick={inc}>+</button>
<button onClick={dec}>-</button>
</>
);
};
const App = () => (
<>
<Count />
<Buttons />
<Count />
<Buttons />
</>
);
export default App;
I am learning meteor and react from building a social network tutorial series. There a tutor had used reactjs in es5 way. He is using several mixins. For example he has used ReactMeteorData mixins with only current user fetching for Navbar and SignupForm Component and same mixin is used but fetching posts, ads and friends in Main.jsx. How could i solve this in es6 way? I have tried using react-meteor-data but confuse with same mixin with different functionality in Navbar and Main component.
Main.jsx
Main = React.createClass({
mixins: [ReactMeteorData],
getMeteorData(){
let data = {};
data.posts = [];
data.ads = [];
var friends = Meteor.user() ? Meteor.user().profile.friends : [];
friends.push(Meteor.user() ? Meteor.user()._id :'');
var posthandle = Meteor.subscribe('postlist', friends,this.state.limit);
var adhandle = Meteor.subscribe('adlist');
if(posthandle.ready()){
data.posts = Posts.find({},{sort:{createdAt:-1}}).fetch();
}
if(adhandle.ready()){
data.ads = DBAds.find({},{}).fetch();
}
return data;
}
render(){
var posts = this.data.posts.map(function (record) {
return <Post key={record._id} post={record}/>
});
return(
);
}
Navbar.jsx
Navbar = React.createClass({
getInitialState(){
return {
searchText:''
};
},
mixins: [ReactMeteorData],
getMeteorData(){
let data = {};
data.currentUser = Meteor.user();
return data;
},
render(){
var fullname = '';
if(this.data.currentUser && this.data.currentUser.profile){
fullname = this.data.currentUser.profile.firstname + ' ' + this.data.currentUser.profile.lastname;
}
return ( );
}
Now my code in es6 is
ReactMeteorDataWrap.jsx
import Navbar from './navbar/Navbar.jsx';
import { createContainer } from 'meteor/react-meteor-data';
export default createContainer(() => {
return { user: Meteor.user() };
}, Navbar);
Main.jsx
import ReactMeteorDataWrap from '../ReactMeteorDataWrap.jsx';
export default class Main extends Component {
constructor(props){
super(props);
}
render() {
// let data = this.props.getMeteorAllData();
let adobj = {_id:1, text:'My First Ad', title:'Some Company', image:'http://placehold.it/350x150' };
let posts = data.posts.map(record => {
return <Post key={record._id} post={record} />
});
return()
}