I am new to react. I write a script to loop a json file(../a.json) to show cities on screen. Inside a.json, it's a list of city information in UK.
Such as :
["Avon",
"Bedfordshire"
...
...
"Tyrone"
]
The code will loop this a json file and show each city in the screen.
Now I want to add a param to let people choose which country to show. I add another 4 json file,
import b from "../b.json";
import d from "../d.json";
import e from "../e.json";
import f from "../f.json";
I also created enum type Locale, but I don't know how to adjust code in code file 3. Would you help take a look?
Can I create a Map in code file 3, and based on what locale passed from Home, I can provide json file. Does it work? How should I adjust code?
Code file 1
Code file 3
You can do something like the below code. Add all the locals in an object. Then onChange of select update the localData.
import React from "react";
import ES from "../data/ES.json";
import IT from "../data/IT.json";
import UK from "../data/UK.json";
const locals = {
UK: UK,
ES: ES,
IT: IT
};
class App extends Component {
constructor() {
super();
this.state = {
index: 0,
selectedLocal: "UK",
localData: locals["UK"]
};
}
onChange = e => {
this.setState({
index: 0,
localData: locals[e.target.value],
selectedLocal: e.target.value
});
};
render() {
return (
<div>
<select value={this.state.selectedLocal} onChange={this.onChange}>
{Object.keys(locals).map(local => {
return <option value={local}>{local}</option>;
})}
</select>
<div className="hints-on-home-screen">
<div
style={{
fontSize: this.props.testFontSize
}}
>
{this.state.localData[this.state.index]}
</div>
</div>
</div>
);
}
}
In your Home component, create a select with all countries as options. Maintain a state and update it upon onChange and pass it on to your Test component.
Home Component
import * as React from "react";
import { Locale } from "../component/testLocale";
type Props = {
testLocale: Locale.UK
}
export class Home extends React.Component<Props, any> {
state = {
testLocale: Locale.UK
}
handleChange = (e) => {
this.setState({testLocale: e.target.value})
}
render() {
return (
<div className="home-background">
<select onChange={this.handleChange} value={this.state.testLocale}>
{Object.keys(Locale).map(country => <option value={Locale[country]}>{country}</option>)}
</select>
<Test testLocale={this.state.testLocale}/>
</div>
);
}
}
what you want to do is add some way to get the chosen json from the user, a Picker (its a dropdown list) is a good choice for this one, feed it the enum lists you have, and make a state called local or something, you'll use this state to handle the selected local, so you'll have something like this for your Picker onValueChange
onValueChange={(itemValue, itemIndex) => setSelectedLocal(itemValue)}
Note that react native uses the hook api in your case, you do something like:
onValueChange={(itemValue, itemIndex) => this.setState({locale: itemValue})}
and use local as a prop for Test
<Test testLocale={this.props.local}/>
See this solution,
Pay careful attention to how everything is configured.
This is a common strategy to manipulate elements using state and data structures.
Stackblitz Example Solution
Related
Problem
When i change the tag value it only changes on the select component but not in the index.astro
I have folder signals where i export signal
export const tagSignal = signal<string>("all");
I use it like this in Select.tsx component, and here evryting changes
import { tagSignal } from "#signal/*";
const setTagValue = (value: string) => {
tagSignal.value = value;
console.log("select", tagSignal.value);
};
export const Select = () => {
const [display, setDisplay] = useState(false);
const [selectedName, setSelectedName] = useState("all"); // this will be change to only signals still under refator
setTagValue(selectedName);
-------
------
but when I import it to index.astro like this I get only "all" value witch is inital value
---
import { Icon } from "astro-icon";
import { Picture } from "astro-imagetools/components";
import Layout from "#layouts/Layout.astro";
import { Select } from "#components/Select";
import Card from "#components/Card.astro";
import { getCollection } from "astro:content";
import { getProjectsByTag } from "#utils/*";
import { tagSignal } from "#signal/*";
const projects = await getCollection("projects");
const filteredProjects = getProjectsByTag(projects, tagSignal.value);
// TODO: add links
console.log("index", tagSignal.value);
---
/// some code here
<section id="projects" class="projects">
<Select client:only="preact" />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</section>
---
I see two issues here.
You are depending on dynamic JS variables in an .astro file. It doesn't work the way you are expecting—all the javascript in .astro files, with the exception of the "islands," e.g., your Select.tsx component, is being evaluated when the page is being built. So Astro grabs the initial value of tagSignal, but makes it a static string.
People can get bitten by, e.g., the trivial ©2010—{new Date().getFullYear()} in the footer – it won't magically update on the new year's eve if used in .astro file.
The state (signal's current value) is not shared accross the islands. If you want to share it, you need either a global state solution (I haven't used it in Astro yet), or just create a common parent for the Select and the filtering logic, e.g.:
{/* the signal will be declared (or imported) in `DynamicSection`*/}
<DynamicSection client:only="preact">
<Select />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</ DynamicSection>
(The simplest global state solution would be probably using the url with a query string, and derive the state from its value).
This is how my app looks like right now:
The search and sort functions are working dynamically, as soon as I type a letter, it finds the match but re-renders all of them.
If I type "hunger", it finds the hunger games films, but it's getting images and rendering when it already has them. Is there any way to make this process just for once so I don't wait for every search, every sorting? I use Redux so data is coming from store, store is getting the data from local json file.
I thought about storing on local storage but I couldn't figure it out. As you can see there is no ComponentDidMount or Hooks.
This is the code of this page:
import React from "react";
import "./MoviesAndSeries.css";
import ListShowItem from "./ListShowItem";
import { getVisibleShows } from "../Redux/Selector";
import { connect } from "react-redux";
import FilterShowItems from "./FilterShowItems";
const Movies: React.FC = (props: any) => {
return (
<div className="movies">
<FilterShowItems />
{props.movies.map((movie: any) => {
return <ListShowItem key={Math.random()} {...movie} />;
})}
</div>
);
};
const mapStateToProps = (state: any) => {
return {
movies: getVisibleShows(state.movies, state.filters),
filters: state.filters,
};
};
export default connect(mapStateToProps)(Movies);
You are using Math.random() as keys. The keys change all the time, and React can't know if the items already exist, so it re-renders all of them:
<ListShowItem key={Math.random()} {...movie} />;
Change the key to something stable, and unique, the movie id (if you have one) for example:
<ListShowItem key={movie.id} {...movie} />;
I'm following a tutorial on React.js, making a Todo app as an example.
App.js
import React, { Component } from 'react';
import TodoItem from "./components/TodoItem";
import Data from "./Data";
class App extends Component {
constructor(){
super();
this.state = { todos:[Data] }
}
render() {
const TodoItems = this.state.todos.map(item =>
<TodoItem key={item.id} item={item} />)
return (
<div>
{todoItems}
</div>
)
}
}
export default App;
TodoItem.js
import React from 'react';
function TodoItem (props) {
return (
<div>
<input type="checkbox" checked={props.item.completed} />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem;
Data.js is simply just an array
const Data = [{id: 1, text: "Some random text", completed: true}, //and so on... ]
When I run this, the browser only renders a checkbox, nothing else. Is there something I'm missing? I checked the dev tools by chrome and saw there are props being passed.
The problem is this: this.state = { todos:[Data] }
That doesn't put the contents of Data in todos, like I think you intend to do, it makes todos an array containing Data, which itself is an array, i.e:
todos = [ [ {id: 1, text: "Some random text", completed: true}, ... ] ]
So when you map over this.state.todos, the 'item' you pull out is actually the single item within todos which is in fact the whole Data array! (not the items within Data like you want)
The array has no text property, so no text shows. It also of course has no completed property, but the checkbox does not need that property to exist to get rendered, so you just see one, single, checkbox with no text.
Change it to this, and it should work.
this.state = { todos: Data }
An unrelated thing, sure just a copy/paste typo in the code here, but just for completeness, you have const TodoItems = ... but then reference {todoItems} in the JSX. I guess that should be const todoItems = ....
First and the foremost thing is that, you need to export data from the Data.js file. Secondly, how you have shown your Data variable, i'm assuming it is an array and thus you can set it directly in the state without encapsulating it in squared brackets.
I have also updated the TodoItem to make sure the text is inline with the checkbox.
Check out the code sandbox link below!
I am making a trivia game in react using the Open Trivia Database API and I am trying to get it so there is a selector button and when it drops down it will show you a list of categories and when you click on it, it will give you a question from said category. I feel like I am close but am just missing a piece and was wondering if someone could spot it or just point me in the right direction? Thank you in advance for your time and help.
Here is my code for app.js:
import React from 'react'
import './App.css'
// import axios from 'axios'
import Questions from './components/questions'
import Categories from './components/categories'
import categorySelector from './components/categorySelector'
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
questions: [],
categories: []
// score: 0,
}
}
render () {
return (
<div className='App'>
<h2>Trivia Game</h2>
<categorySelector
categories={Categories}
onSelect={event => this.questions(event.target.value)}
/>
</div>
)
}
}
export default App
And here is my code for categorySelector.js:
import React from 'react'
class categorySelector extends React.Component {
render () {
const { categories, onSelect } = this.props
return (
<div class='categorySelector'>
<select
value={categories} onChange={onSelect}
>
<option value=''>-- No category selected --</option>
{categories.map(category => (
<option value={category} key={category.id}>{category.name}</option>
))}
</select>
</div>
)
}
}
export default categorySelector
It looks like the select tag does not take a value prop: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select
I modified your return statement as follows
change 'class' to 'className' as class is a reserved keyword in Reactjs
removed value prop from select tag
updated the value prop in the options tag to be the category name instead of object
return (
<div class='categorySelector'>
<select onChange={onSelect}>
<option value=''>-- No category selected --</option>
{categories.map(category => (
<option value={category.name} key={category.id}>{category.name}</option>
))}
</select>
</div>
)
I also suggest capitalizing react class names such as CategorySelector instead of categorySelector.
There's a couple typo's in the App component
You're passing the Categories component as a prop instead of passing the categories slice of state.
In the onSelect event, this.questions(...) implies you're calling a method. However it doesn't look like you have a questions method written.
Instead, if you want to 'save' the selected category in the questions list you can call setState like so:
The code inside the brackets adds the selected value without removing previous selections.
<categorySelector
categories={this.categories}
onSelect={event => this.setState({questions: [...this.state.questions, event.target.value]})}
/>
I am new to react js and I am trying to create a pivot table using React Pivot table. I want to be able to select a data set using a select drop down menu and update the state and re render the full table whenever there is a change in the drop down selection just like the jquery example shown here https://pivottable.js.org/examples/rcsvs.html
It works before I make any selections or changes to the Pivot Table. I am able to toggle between the 2 datasets and the state changes in the Pivot table. But when I select a pivot dimension and use the pivot table, after that point, changing the select menu does not help me change the pivot table's state. Please help.
Here's my code.
import React from 'react';
import PivotTableUI from 'react-pivottable/PivotTableUI';
import 'react-pivottable/pivottable.css';
import TableRenderers from 'react-pivottable/TableRenderers';
import Plot from 'react-plotly.js';
import createPlotlyRenderers from 'react-pivottable/PlotlyRenderers';
// create Plotly renderers via dependency injection
const PlotlyRenderers = createPlotlyRenderers(Plot);
const data1 = [{'Country':'USA','Sales':45000},
{'Country':'USA','Sales':50000},{'Country':'CA','Sales':15000}]
const data2 = [{'Product':'Sofa','Sales':5000},{'Product':'Dinner
Table','Sales':50000},{'Product':'Chair','Sales':15000}]
const dataDic = {'Region':data1,'Products':data2}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {selectedOption: 'Region'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({selectedOption: event.target.value});
}
handleSubmit(event) {
alert('You have selected ' + this.state.selectedOption);
event.preventDefault();
}
render() {
return <div>
<select defaultValue="Region" onChange={(e)=>
this.handleChange(e)}>
<option value="Region">Region</option>
<option value="Products">Products</option>
</select>
<br/>
<br/>
<PivotTableUI
data={dataDic[this.state.selectedOption]}
onChange={s => this.setState(s)}
renderers={Object.assign({},TableRenderers)}//,PlotlyRenderers)}
{...this.state}
/>
</div>;
}
}
export default App;
I found that if I delete the data property from s
<PivotTableUI
data = {[{}]}
onChange={ s =>{
delete s.data
this.setState(s)
}}
{...this.state}
/>
This won't overwrite the data in the parent class and will be automatically rendered with updated data
Working code sandbox
There were two problems;
s => this.setState(s) in your PivotTable's onChange property.
This overrides root state with all the props of your PivotTable.
When your page initiated, container's (Grid) state only contains selectedOption:"Region" but after interacting with the PivotTable, container has receives all the props of the PivotTable. Screenshot:
{...this.state} prop in PivotTableUI component, passes all keys as props in container's state including data.(As seen in the screenshot above). And this overrides data property, data={dataDic[this.state.selectedOption]} After this, changes to selectedOption state does not re-render PivotTableUI
Solution
Change s => this.setState(s) with this.setState({ pivotTableUIConfig: s });
Define a pivotTableUIConfig variable which does not include data property. (Used ES7 Object Rest Operator to Omit data property)
// Picking all the properties except "data"
const { data, ...pivotTableUIConfig } = this.state.pivotTableUIConfig;
Change {...this.state} with {...pivotTableUIConfig}
Encountered the same problem and found a simple fix. The problem is that data prop always gets overwritten by {...state}. Hence we can assign data to state before passing it to PivotTableUI. And the data prop is no longer needed.
const state = {...this.state};
state['data'] = dataDic[this.state.selectedOption];
return (
<PivotTableUI
onChange={s => this.setState(s)}
{...state}
/>
)
you should add
renderers={{ ...TableRenderers, ...PlotlyRenderers }}
{...state}
to your PivotTableUI, see the full example:
import TableRenderers from 'react-pivottable/TableRenderers';
import PivotTableUI from 'react-pivottable/PivotTableUI';
import Plot from 'react-plotly.js';
import createPlotlyRenderers from 'react-pivottable/PlotlyRenderers';
import 'react-pivottable/pivottable.css';
const PlotlyRenderers = createPlotlyRenderers(Plot);
function DataExploration() {
const { dashboardData: { data } } = useSelector((state) => state.dashboardData)
const ExpComp = (props) => {
const [state, setState] = useState(props);
return (
<div className="row">
<div className="col-md-12">
{
<PivotTableUI
style={
{
width: "100%",
height: "100%"
}
}
onChange={(s) => setState(s)}
renderers={{ ...TableRenderers, ...PlotlyRenderers }}
{...state}
/>
}
</div>
</div>
)
}
return (
<ExpComp data={data} />
)
}
From more details check the doc: https://github.com/plotly/react-pivottable