react create dyamic menu by settings - javascript

I'm have side bar navigation that get a list of json
import {
AppSidebarNav,
} from '#coreui/react';
<AppSidebarNav navConfig={navigation} {...this.props} />
and the data is the list of items
export default {
items: [
{
name: 'Dashboard',
url: '/dashboard',
icon: 'icon-speedometer',
},
{
name: 'Profile',
url: '/profile',
icon: 'icon-speedometer',
},...
],
};
how can I set the list of items before they load to the sidebar ?
there is any way to use componentDidMount()
to update the list ?
how should I approch this task

Put the list of items in its own file, say, nav.js. Then add this line to your imports
import navigation from "./nav"; // or whatever relative path nav.js is in.

You could use this sample code for fetch dynamic data by async, await and axios in AppSidebarNav core-ui
import React, {Component, Suspense} from "react";
import {
AppSidebarNav,
AppSidebar,
AppSidebarFooter,
AppSidebarForm,
AppSidebarHeader,
AppSidebarMinimizer,
} from "#coreui/react";
import axios from "axios";
import navigation from "../../_nav";
class SidebarNav extends Component {
constructor(props) {
super(props);
this.state = {
navData: null,
};
}
async getData() {
let res = await axios.get('REQUEST_URL');
this.setState({ navData: {items : res });
// OR // return await {items : res};
};
componentDidMount() {
if (!this.state.navData) {
(async () => {
try {
await this.getData();
// OR // this.setState({navData: await this.getData()});
} catch (e) {
//...handle the error...
console.log(e);
}
})();
}
}
render() {
return (
<AppSidebar fixed display="lg">
<AppSidebarHeader />
<AppSidebarForm />
<Suspense>
<AppSidebarNav navConfig={(this.state.navData === null) ? navigation : this.state.navData} {...this.props} />
</Suspense>
<AppSidebarFooter />
<AppSidebarMinimizer />
</AppSidebar>
)
}
}
export default SidebarNav;
}
navigation variable could be used for default data or loading...
export default {
items: [
{
name: 'Loading... ',
url: '/'
}
]
}
In the last part call SidebarNav Component in DefaultLayout or anywhere.

Old question, but for anyone wondering how to do this, I moved forward by doing the following:
Parent:
Setup constructor and added currentNavigation prop
Default new prop to default navigation
Change AppSidebarNav to use new prop
Create a function in the parent that takes a nav object, and updates the new currentNavigation prop
In this example, I add my navigation object to a property called index in a new object, this is required for the CoreUI nav it seems, however, you could do this in your child component / when creating the navigation object you will be using to update the menu.
/** Parent **/
import navigation from "../../navigation/_nav"
class DefaultLayout extends Component {
constructor(props) {
super(props)
this.currentNavigation = navigation //Set to default navigation from file
}
functionFromParent = (yourNavObject) => {
// nav contains the navigation object (build this however you do),
// but needs to be added to { items: ... }
let data = {
items: yourNavObject
}
this.currentNavigation = data;
}
render = () => {
return(
// ...More code
<AppSidebarNav
navConfig={this.currentNavigation} //Added function that child fires to prop
{...this.props}
router={router}
/>
// More code...
}
}
}
Child:
Get navigation object however you generate it to update the menu
Pass navigation object through to the functionFromParent using props (this.props.functionFromParent(this.getYourNavigationObject()))
/** Child **/
class ChildComponent extends Component {
// ... More Code
componentDidMount = () => {
let navigation = this.getYourNavigationObject() //Get your navigation object however you do
this.props.functionFromParent(navigation)
}
// More Code ...
}

Related

React component not updating after props change

I am making a newsapp in react.js following tutorial, now i want to add different categories to my app the man in the tutorial was using react-router-dom for navigating but I want to set a state named category and category as it's key and pass it to news component as props, i have made a function in app.js for selecting category which takes an argument and sets it as category's (the state's) key and i am passing that function to navbar as props, so i can use it in navbar when a category is clicked I can change the state but when i change the category nothing happens some will say use key but that's not the problem because I'm doing it, when I tried console logging the category in the setCategory function it is being logged continuously and the state doesn't change when i click on any of the category what am i doing wrong
my code:
import './App.css';
import React, { Component } from 'react'
import Navbar from './components/Navbar';
import News from './components/News';
export default class App extends Component {
constructor() {
super()
this.state = {
darkMode: "light",
country: "us",
category: "general",
key: "general"
}
}
setCategory = (cat)=> {
this.setState({
category: cat
})
console.log(this.state.category)
}
setCountry = (cntry)=> {
this.setState({
category: cntry
})
}
setDarkMode = () => {
if (this.state.darkMode === "light") {
this.setState({ darkMode: "dark" })
document.body.style.backgroundColor = "black"
} else {
this.setState({ darkMode: "light" })
document.body.style.backgroundColor = "white"
}
}
render() {
return (
<div>
<Navbar setCategory={this.setCategory} setCountry={this.setCountry} setDarkMode={this.setDarkMode} darkMode={this.state.darkMode} />
<News key={this.state.category} category={this.state.category} country={this.state.country} pageSize={18} darkMode={this.state.darkMode} />
</div>
)
}
}

this.props Not Returning Fetched Data From Parent Component (React)

I am attempting to render playlist information for an Audio Player in React. The data is coming from a fetch call in the parent component (PostContent.js). The data being returned is an array of objects that looks like:
[ {name: ‘track name’, artist: ‘artist name’, url: ’https://blahblah.wav', lrc: ‘string’, theme: ‘another string’ }, {…}, {…}, etc. }
I am not able to return the data in the render() method of the child component (AudioPlayer.js). When I console.log(this.props.audio) in the render(), my terminal prints three responses. The first is an empty array, and the next two are the correct data that I need (an array of objects).
How can I set the props on the ‘audio’ key in the ‘props’ object in the render() method of the AudioPlayer.js component?
I should mention that I am using the react-aplayer library, and I am able to make this work with hard-coded data, as in the example here (https://github.com/MoePlayer/react-aplayer), but I am trying to make a dynamic playlist component for a blog website. Any advice is greatly appreciated.
AudioPlayer.js (Child Component)
import React, { PureComponent, Fragment } from 'react';
import ReactAplayer from '../react-aplayer';
import './AudioPlayer.css';
import sample from '../../src/adrian_trinkhaus.jpeg';
export default class AudioPlayer extends React.Component {
// event binding example
onPlay = () => {
console.log('on play');
};
onPause = () => {
console.log('on pause');
};
// example of access aplayer instance
onInit = ap => {
this.ap = ap;
};
render() {
console.log('props in render of AudioPlayer', this.props.audio)
const props = {
theme: '#F57F17',
lrcType: 3,
audio: this.props.audio
};
return (
<div>
<ReactAplayer
{...props}
onInit={this.onInit}
onPlay={this.onPlay}
onPause={this.onPause}
/>
</div>
);
}
}
PostContent.js (Parent Component)
import React, { Component, useState, Fragment } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import AudioPlayer from './AudioPlayer';
export default class PostContent extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
episodeData: [],
audio: []
}
}
async componentDidMount() {
const { id } = this.props.match.params;
const response = await fetch(`http://localhost:5000/episode/${id}/playlist`);
const jsonData = await response.json();
const songs = jsonData;
const audio = Object.keys(songs).map(key => {
return {
name: songs[key].name,
artist: songs[key].artist,
url: songs[key].url,
cover: songs[key].cover,
lrc: songs[key].lrc,
theme: songs[key].theme
}
});
this.setState({ audio })
}
componentDidUpdate(prevProps, prevState) {
if (prevState.audio !== this.state.audio) {
const newAudio = this.state.audio;
this.setState({ audio: newAudio }, () => console.log('new audio', this.state.audio))
}
}
render() {
return (
<Fragment>
<AudioPlayer audio={this.state.audio} />
<Link id='home-link' to='/' activeClassName='active'>Homepage</Link>
{this.state.episodeData.map((item, i) => (
<div key={i} className="word-content">
<h2 className="show-title">{item.post_title}</h2>
<div className="episode-post-content">
<p>{item.post_content1}</p>
<p>{item.post_content2}</p>
<p>{item.post_content3}</p></div>
</div>
))}
<Table data={this.state.data} />
<div className="bottom-link">
<Link id='home-link' to='/' activeClassName='active'>Homepage</Link>
</div>
</Fragment>
)
}
}
i played around with an async scenario with your code on codesandbox
i think the problem is when you're trying to access the payload in ReactAPlayer component when audio it's not loaded yet from the async call. what you need to do is only use "audio" when it's valid like this if (audio.length) {...} or audio && ... some form of check to prevent it from being accessed in the reactAplayer render function.
fyi - you can remove the componentDidUpdate hook, since you have a setState call inside the ...Didmount hook, when setState is called inside ...didMount, the component calls its render() thus trigger a child re-render and its child will do the same..
Actually I think it doesnt work because you set this.props inside a props obejct, so maybe you need to do something like
var that = this
const props = {
audio = that.props.audio
}

component state's var disappears after changing it outside

redux stores language's locale
translator component gets translations from const via key and saves it to their own state. and returns current language's translation in span
im trying to use this library https://www.npmjs.com/package/baffle for fancy effect.
everything works fine with just plain text in translation component. but text disappears, when text depends on state.
not sure if i explained correctly, so there is a little example.
any text - works
{this.state.etc} - doesn't
Translate.jsx
import { TRANSLATIONS } from './../../constants/translations';
class Translate extends React.Component {
constructor(props) {
super(props);
this.state = {
translations: {},
}
}
componentDidMount() {
const translationKeys = this.props.translationKey.split('.');
let translation = TRANSLATIONS;
translationKeys.forEach((i) => {
translation = translation[i]
});
this.setState((state) => ({
translations: translation,
}));
}
render() {
return (
<span ref={this.props.translateRef}>{this.state.translations[this.props.locale]}</span>
)
}
}
Translate.propTypes = {
locale : PropTypes.string.isRequired,
translationKey: PropTypes.string.isRequired,
}
export default Translate;
TranslateContainer.js
const mapStateToProps = state => ({
locale: state.locale,
})
export default connect(mapStateToProps, null, null, {forwardRef: true})(Translate);
and im using this component in react-router-dom custom links
import { Route, Link } from 'react-router-dom';
import classNames from 'classnames';
import baffle from 'baffle';
import css from './RouteLink.module.css';
import Translate from '../Translations/TranslateContainer';
class RouteLink extends React.Component {
constructor(props) {
super(props);
this.baffleRef = React.createRef();
this._onMouseEnter = this._onMouseEnter.bind(this);
}
componentDidMount() {
this.baffle = baffle(this.baffleRef.current);
};
_onMouseEnter() {
this.baffle.start();
}
render() {
return (
<Route
path={this.props.to}
exact={this.props.active}
children={({ match }) => (
<Link
to={this.props.to}
className={classNames({
[css.link]: true,
[css.active]: match,
})}
onMouseEnter={this._onMouseEnter}
>
<Translate
translationKey={this.props.label}
translateRef={this.baffleRef}/>
</Link>
)}
/>
);
}
}
RouteLink.propTypes = {
to : PropTypes.string.isRequired,
label : PropTypes.string.isRequired,
active: PropTypes.bool,
}
export default RouteLink;
translations.js
export const TRANSLATIONS = {
navBar: {
home: {
ru_RU: 'Главная',
en_EN: 'Home',
},
},
}
Is there any way to fix this?
Translations works just fine, switching language works.
Links work.
And if translator returns span without state, even baffle works.
But if translator returns text that depends on state, text just disappears, when baffle starts any function
I don't see the need for the forEach loop in componentDidMount.
The line below will return an array of 2 elements ['navbar','home']
const [firstLevel,secondLevel] = this.props.translationKey.split('.') // array destructuring to get the two values
You could just use the values in the array and locale props to set your translation state.
this.setState({translations: TRANSLATIONS[firstLevel][secondLevel][this.props.locale]})
Then in your render,just use your state
<span ref={this.props.translateRef}>{this.state.translations}</span>
This would solve the issue but as a sidenote,you can do the splitting of props.translationKey and traversing the TRANSLATIONS object even within the render instead of componentDidMount.You don't need the state as you don't seem to have an event handler to setState again.

Passing prop to another component and render on the view

import Form from './Form'
class SideBar extends React.Component {
constructor(props) {
super(props);
this.state = {
item: ''
};
}
render() {
return (
this.props.products.map((x) => {
let boundItemClick = this.onItemClick.bind(this, x);
return <li key={x.id} onClick={boundItemClick}>{x.id}-{x.style_no}-{x.color}</li>
}));
}
onItemClick= function(item, e) {
console.log(item)
}
}
export default SideBar
import SideBar from './SideBar'
class Form extends React.Component{
render(){
return();
}
}
export default Form
**strong text**<div class = first_half><%= react_component("SideBar", {products:
#products}) %></div>
<div class = second_half><%= react_component("Form", {products:
#products}) %></div>
I have a question about how to pass the props to Form component. Right now, the SideBar component list all the link, and everything I click one of the link, I can console.log the information. But I have no idea how to pass it on the other component and render on the view. Thank you
for example : (this is a sidebar componenet)
301-abc
302-efg
303-rgk
When user click 301-abc, it will show coresponding details like id, color, and style.
Likewise SideBar component you have to create constructor a Form component.
And you have to create state with products detail.
class SideBar extends React.Component {
constructor(props) {
super(props);
this.state = {
item: ''
};
}
render() {
return (
this.props.products.map((x) => {
let boundItemClick = this.onItemClick.bind(this, x);
return <li key={x.id} onClick={boundItemClick}>{x.id} - {x.style_no} - {x.color}</li>
}));
}
onItemClick = function(item, e) {
console.log(item)
this.props.selectedData(item);
}
}
export default SideBar
import SideBar from './SideBar'
class Form extends React.Component {
constructor() {
this.state = {
products: [{
id: '302',
style_no: 'abc',
color: 'red'
}, {
id: '303',
style_no: 'abcd',
color: 'black'
}],
selectedData: {}
};
}
getSelectedData(selectedData) {
this.setState({
selectedData: selectedData
});
}
render() {
return (
<SideBar products = {this.state.products} selectedData={this.getSelectedData}>
);
}
}
In above code I have pass method as pops with name selectedData and you can use that in your onItemclick method as I used it and pass your item.
this.props.selectedData(item);
So, you will get that item in your Form component and set your state and you can display it in your Form component and can also pass to another component.
Hope it will work for you..!!
If you got a lot of nested components which need to be aware of each other state/information, your best choice is to choose a state management something like redux,flux or etc. state management mission is to hold data across components and helps you keep uni-direction data-flow in react.
Although, if you don't want to use a state management mechanism for any reason. you may implement it like this (which is ugly):
https://stackblitz.com/edit/react-pkn3cu

How to send this.state from a component to relay root container

I want to change my root query parameter based on the this.state.eventid which is a child component, but I have no clue how to get props to relay root container. I started based on relay-starter-kit.
I have React component that has a dropdown menu, and onSelect it setStates for eventId
renderAttend() {
if (this.props.groups != null && this.state.success != true) {
var events = this.props.events.map(function(event){
var boundSelect = () => {this.setState({eventid:event.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={event.nodes[0].id} onSelect={boundSelect.bind(this)}>{event.nodes[0].properties.summary} / {event.nodes[0].properties.start}</MenuItem>
},this)
var teams = this.props.groups.map(function(team){
var boundSelect = () => {this.setState({teamid:team.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={team.nodes[0].id} onSelect={boundSelect.bind(this)}>{team.nodes[0].properties.name}</MenuItem>
},this)
return (
<div>
<ButtonGroup>
<DropdownButton style={{padding:"15px",fontSize:20}}title="Events" id="bg-vertical-dropdown-2">
{events}
</DropdownButton>
<DropdownButton style={{padding:"15px",fontSize:20,marginLeft:"5px"}} title="Groups" id="bg-vertical-dropdown-2">
{teams}
</DropdownButton>
</ButtonGroup>
</div>
)
}
}
I want to use this state to somehow change my root query...
my approute...
import Relay from 'react-relay';
export default class extends Relay.Route {
static paramDefinitions = {
eventId: {required: false}
};
static queries = {
Event : () => Relay.QL`query{eventState(eventId:$eventId)}`,
};
static routeName = 'AppHomeRoute';
}
and my app.js
import 'babel-polyfill';
import App from './components/App';
import AppHomeRoute from './routes/AppHomeRoute';
import React from 'react';
import ReactDOM from 'react-dom';
import Relay from 'react-relay';
ReactDOM.render(
<Relay.RootContainer
Component={App}
route= {new AppHomeRoute}
renderLoading={function() {
return <div style= {{display:"flex",justifyContent:"center",marginTop:"55px"}}> <h1>Loading...</h1></div>;
}}
renderFailure={function(error, retry) {
return (
<div>
<h1>Click Refresh</h1>
</div>
);
}}
/>,
document.getElementById('root')
);
Now I want to this.state.eventid from the react component to update my root query, but I have no idea how to pass data from child component to react root.container. I do not want to use react-router for this :)
p.s. this.props.events were passed to me by an ajax call so they are not saved in relay/graphql data.
For such a case, the better thing to do is to wrap your root query into a story like
{
store {
events(eventId:$eventId)
}
}
So in the root query you only have
export default class extends Route {
static queries = {
app:() => Relay.QL`query { store }`
};
static routeName = "AppRoute";
}
And in the page you create a fragemnt like
let RelayApp = createContainer(SomeComponent, {
initialVariables: {
eventId: null
},
fragments: {
app: () => Relay.QL `
fragment on Store {
id
events(eventId: $eventId) {
pageInfo {
hasNextPage
}
edges {
cursor
node {
name
...
}
}
}
}
`,
},
});
export
default RelayApp;
For the child component, you set the eventId and onChange event handler as props from parent component. And in the parent componet you implement the event handler and call this.props.setVariables({eventId: someVal}) like
// Child Component
export default class Menu extends Component {
render() {
return(
<ul>
<li onClick={() => this.props.selectItem(val)}>{val}</li>
...
</ul>
)
}
}
// Parent Component
class Main extends Component {
_selectItem = (val) => {
this.props.relay.setVariables({eventId: val});
}
render() {
return(
<div>
<Menu selectItem={() => this._selectItem}/>
</div>
)
}
}
let RelayApp = ...
export default Main
Hope this will help.
There is no easy way to solve this. Either use react-router-relay or nest your query like this and use this.props.relay.setVariables()
viewer {
eventState(eventid:$eventid) {
data
}
}

Categories

Resources