TODO project is not woking properly - reactjs

Components ->
Box
Todolist
Add
AddModal
Main component App
But it is not working that is when I add a new task. It does not get added properly.
I think I cannot use this.setstate twice in a function.
Hope I am correct
Here is given the main component.
App.js :
import React, { Component } from 'react';
import './App.css';
import Box from './Components/Box';
import Add from './Components/Add';
import Todolist from './Components/Todolist';
class App extends Component {
constructor(props) {
super(props);
this.state = {
lists: '',
inputValue: '',
itemArray: []
}
}
onAddTask = () => {
this.setState ({
lists: this.state.inputValue
});
const item = this.state.itemArray;
const title = this.state.lists;
item.push({ title })
this.setState(prevState => ({
itemArray: [...prevState.lists, title]
}))
}
updateInputValue = (event) => {
this.setState({
inputValue: event.target.value
});
}
render() {
let length = this.state.itemArray.length;
return (
<div className="App">
<Box createTodo = {
<div>
{this.state.itemArray.map((itemArr) => {
return (
<div className="box">
<Todolist tasks = {itemArr} />
</div>
)
})}
</div>
}>
</Box>
<Add addTask = {this.onAddTask} inputValues = {this.updateInputValue} inputV = {this.state.inputValue} />
</div>
);
}
}
export default App;

Your addTasks function is not correct, you are mixing up things here.
In your inputValue you save the current value from the input field right? So if you write the following
this.setState({
lists: this.state.inputValue
});
you set your todo list to this single value. And your todo list is not an array anymore.
Secondly, state is imutable. So if you write the following
this.state.itemArray.push({ title });
the state will not be updated. What you actually want is the following:
onAddTask = () => {
this.setState({
itemArray: [...this.state.itemArray, this.state.inputValue]
})
}
And I'm not sure what the lists property on the state is for. You don't use it anywhere besides in your onAddTask function. So I guess you can remove it.

Related

How can I update a single React Component from multiple different components?

I'm learning React and still trying to figure out how to plan out and implement some things. I have an app that makes three different API calls and thus three different return values. I'd like to have a global status component that tells me if all three loaded or not. Here's my psuedo code since I haven't found the proper way to do this yet, but this is effectively my train of thought at the moment. I have the main app:
const App = () => {
return (
<div>
<GenericAPICallerA />
<GenericAPICallerB />
<GenericAPICallerC />
<div>
<APIStatus/>
</div>
</div>
);
}
This is the APIStatus which just returns if all A, B, and C API calls have loaded or not:
class APIStatus extends React.Component{
constructor(props){
super(props);
this.state = {
aLoaded: false,
bLoaded: false,
cLoaded: false,
};
}
render(){
if (this.state.aLoaded && this.state.bLoaded && this.state.cLoaded){
return <div>Everything has loaded!</div>
}
else{
return <div>Nothing has loaded!</div>
}
}
}
And finally one of the APICaller components. The others are essentially the same:
class GenericAPICallerA extends React.Component{
constructor(props){
super(props);
this.state = {
error: null,
isLoaded: false,
};
}
componentDidMount(){
fetch("example.com/api",{
method: 'GET',
})
.then(res => res.json())
.then(
(result) =>{
this.setState({
isLoaded: true,
});
},
(error) =>{
this.setState({
isLoaded: false,
error
});
}
)
}
render(){
const { error, isLoaded, profile } = this.state;
if (error){
return <div>Errored!</div>
} else if (!isLoaded){
// APIStatus.state.aLoaded=false
} else {
// APIStatus.state.aLoaded=true
return(
<div>Generic Caller A is done!</div>
);
}
}
}
The comments in the render section are what I don't know how to do. I feel like I should pass in the APIStatus as a prop to the GenericAPICaller but I'm still unsure how I would update that property from inside the GenericAPICaller.
Thanks!
You can create a function in parent component and pass it to the child will be triggered and pass a state variable to the child where it will be used
For example:
import React from 'react'
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
class App extends React.Component
{
constructor()
{
super()
this.state = { my_state_variable:`some value` }
}
my_function()
{
this.setState({ my_state_variable:`new value` })
}
render = () => <>
<ComponentA my_function={my_function} />
<ComponentB my_state_variable={my_state_variable} />
</>
}
export default App
ComponentA
import React from 'react'
const ComponentA = ({ my_function }) => <>
<button onClick={() => my_function() }>Click Me </button>
</>
export default ComponentA
ComponentB
import React from 'react'
const ComponentB = ({ my_state_variable }) => <>
<p>{my_state_variable}</p>
{my_state_variable === `some value` && <p>if some value this will render </p>}
{my_state_variable === `new value` && <p>if new value this will render </p>}
</>
export default ComponentA
You can use context to accomplish this. By using context, you are able to access the value you provide to it as long as the component you attempt to access it through is a child of a provider.
The example below illustrates how you can access a shared state between multiple components at different levels without having to pass props down.
const {
useState,
useEffect,
createContext,
useContext,
Fragment
} = React;
const MainContext = createContext({});
function Parent(props) {
const [state, setState] = useState({child1:false,child2:false,child3:false});
return <MainContext.Provider value={{state,setState}}>
<Child1/> {state.child1? 'loaded':'not loaded'}
<br/>
<Child2/>
</MainContext.Provider>;
}
function Child1(){
const {state, setState} = useContext(MainContext);
return <button onClick={()=>setState({...state, child1:!state.child1})}>Load Child 1</button>;
}
function Child2(){
const {state, setState} = useContext(MainContext);
return <Fragment>
<button onClick={()=>setState({...state, child2:!state.child2})}>Load Child 2</button> {state.child2? 'loaded':'not loaded'}
<br/>
<Child3/> {state.child3? 'loaded':'not loaded'}
</Fragment>;
}
function Child3(){
const {state, setState} = useContext(MainContext);
return <button onClick={()=>setState({...state, child3:!state.child3})}>Load Child 3</button>;
}
const el = document.querySelector("#root");
ReactDOM.render(<Parent/>, el);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>

How to keep a new component after Search function is called?

On a React project, I'm trying to link user's spotify playlist to an app page. Until this step, I've created quite a few components to enable user to search for songs, add the ones from the list to his spotify playlist (but user cannot see his previous playlists).
I have added a dummy box to be utilized when I achieve to pull user's playlist info, the problem is when I type a song name and click Search, the box I created for this purpose disappears.
My code previous to adding latest SpotifyPlaylist box can be find in the following: https://github.com/basakulcay/Jammming
This is how it looks like before search:
I cannot find out where I need to make a change to make this happen. I think, it should be related to onSearch, but nothing I tried seemed to be working. *Appreciate the help! :) *
Below is the code from App.js:
import React from 'react';
import './App.css';
import { SearchBar } from '../SearchBar/SearchBar';
import { SearchResults } from '../SearchResults/SearchResults.js';
import { Playlist } from '../Playlist/Playlist.js';
import Spotify from '../../util/Spotify.js';
import { SpotifyPlaylist } from '../spotifyPlaylist/SpotifyPlaylist';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
playlistName: 'My Playlist',
playlistTracks: [],
};
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.search = this.search.bind(this);
}
addTrack(track) {
let tracks = this.state.playlistTracks;
if (tracks.find((savedTracks) => savedTracks.id === track.id)) {
return;
}
tracks.push(track);
this.setState({ playlistTracks: tracks });
}
removeTrack(track) {
let tracks = this.state.playlistTracks;
tracks = tracks.filter((currentTrack) => currentTrack.id !== track.id);
this.setState({ playlistTracks: tracks });
}
updatePlaylistName(name) {
this.setState({ playlistName: name });
}
savePlaylist() {
const trackUris = this.state.playlistTracks.map((track) => track.uri);
Spotify.savePlaylist(this.state.playlistName, trackUris).then(() => {
this.setState({ playlistName: 'New Playlist', playlistTracks: [] });
});
}
search(term) {
Spotify.search(term).then((searchResults) => {
this.setState({ searchResults: searchResults });
});
}
render() {
return (
<div>
<h1>
Ja<span className="highlight">mmm</span>ing
</h1>
<div className="App">
<SearchBar onSearch={this.search} />
<div className="App-playlist">
<SearchResults
onAdd={this.addTrack}
searchResults={this.state.searchResults}
/>
<Playlist
playlistName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove={this.removeTrack}
onNameChange={this.updatePlaylistName}
onSave={this.savePlaylist}
/>
<SpotifyPlaylist />
</div>
</div>
</div>
);
}
}
export default App;

Reset react-select dropdown when new props passed in

I would like to clear the value in the dropdown on getting new props. Currently, the previous value remains visible even though if I expand the dropdown, I can see new values.
import React from 'react';
import Select from 'react-select';
import axios from 'axios';
class KeydateDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
optionList: []
};
};
componentDidUpdate(prevProps) {
let vesselname = this.props.vesselname;
if (prevProps.vesselname !== vesselname) {
let keydateList = [];
this.setState({
optionList: keydateList
});
axios.get('list-keydates', {
params: {
vesselname: vesselname
}
})
.then((response) => {
let data = response.data['intervention'];
data.forEach((element) => {
keydateList.push({ value: element, label: element });
});
this.setState({ optionList: keydateList });
})
}
}
render() {
return (
<Select
isDisabled={this.props.isDisabled}
onChange={this.props.handleKeydateChange}
options={this.state.optionList}
className={styles.dropdown}
/>
);
}
}
export default KeydateDropdown;
I have confirmed that the first setState() inside componentDidUpdate which clears optionList calls render()
Edit: Here's the codesandbox
You forget to explicitly set a value on the Select component. You have to pass this.state.selectedKeydate to KeydateDropdown in MainInputBar and then pass that value to the Select component:
In MainInputBar.jsx:
keydateDropdown = (
<KeydateDropdown
vesselname={this.state.selectedVessel.value}
selectedKeydate={this.state.selectedKeydate}
handleKeydateChange={this.handleKeydateChange}
value={this.state.selectedKeydate}
/>
);
In KeyStateDropdown.jsx:
<Select
isDisabled={this.props.isDisabled}
onChange={this.props.handleKeydateChange}
options={this.state.optionList}
className={styles.dropdown}
value={this.props.value}
/>
I forked your sandbox and fixed it here: https://codesandbox.io/s/happy-einstein-55klv

React trying to make a list of dynamic inputs

I have built this site
https://supsurvey.herokuapp.com/surveycreate/
now I am trying to move the fronted to React so I can learn React in the process.
with vanila js it was much easier to create elements dynamically.
I just did createElement and after that when I clicked "submit" button
I loop throw all the elements of Options and take each target.value input.
so I loop only 1 time in the end when I click Submit and that's it I have now a list of all the inputs.
in react every change in each input field calls the "OnChange" method and bubbling the e.targe.value to the parent and in the parent I have to copy the current array of the options and rewrite it every change in every field.
is there other way? because it seems crazy to work like that.
Options.jsx
```import React, { Component } from "react";
class Option extends Component {
constructor(props) {
super(props);
this.state = { inputValue: "", index: props.index };
}
myChangeHandler = event => {
this.setState({ inputValue: event.target.value });
this.props.onChange(this.state.index, event.target.value);
};
render() {
return (
<input
className="survey-answer-group"
type="text"
placeholder="Add Option..."
onChange={this.myChangeHandler}
/>
);
}
}
export default Option;
______________________________________________________________________________
Options.jsx````
```import React, { Component } from "react";
import Option from "./option";
class Options extends Component {
render() {
console.log(this.props);
return <div>{this.createOptions()}</div>;
}
createOptions = () => {
let options = [];
for (let index = 0; index < this.props.numOfOptions; index++) {
options.push(
<Option key={index} onChange={this.props.onChange} index={index} />
);
}
return options;
};
}
export default Options;```
______________________________________________________________________________
App.jsx
```import React from "react";
import OptionList from "./components/Options";
import AddButton from "./components/add-button";
import "./App.css";
class App extends React.Component {
state = {
numOfOptions: 2,
options: [{ id: 0, value: "" }, { id: 1, value: "" }]
};
handleChange = (index, value) => {
const options = [...this.state.options];
console.log("from App", value);
options[index].value = value;
this.setState({
options: options
});
console.log(this.state);
};
addOption = () => {
const options = [...this.state.options];
options.push({ id: this.state.numOfOptions + 1, value: "" });
this.setState({
numOfOptions: this.state.numOfOptions + 1,
options: options
});
};
submitButton = () => {};
render() {
return (
<div className="poll-create-grid">
<div id="poll-create-options">
<OptionList
onChange={this.handleChange}
numOfOptions={this.state.numOfOptions}
/>
</div>
<button
className="surveyCreate-main-btn-group"
onClick={this.addOption}
>
Add
</button>
<button
className="surveyCreate-main-btn-group"
onClick={this.submitButton}
>
Submit
</button>
</div>
);
}
}
export default App;
```
So firstly,
The issue is with the way your OptionList component is defined.
Would be nice to pass in the options from the state into the component rather than the number of options
<OptionList
onChange={this.handleChange}
options={this.state.options}
/>
The you basically just render the options in the OptionsList component (I'm assuming it's same as the Options one here
class Options extends Component {
...
render() {
return
(<div>{Array.isArray(this.props.options) &&
this.props.options.map((option) => <Option
key={option.id}
index={option.id}
onChange={this.props.onChange}
value={option.value}
/>)}
</div>);
}
...
}
You would want to use the value in the Option component as well.
this.props.onChange(this.state.index, event.target.value); No need using the state here to be honest
this.props.onChange(this.props.index, event.target.value); is fine

Pass state value to component

I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
From that code, i set a state called item. And i want to pass this state to a component. Here is my code :
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
But i get an error that say
TypeError: Cannot read property 'item' of undefined
I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.
Here is my full code for additional info :
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.getListSiswa();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
export default ListSiswa;
One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:
let SelectableList = makeSelectable(List);
function wrapState(ComposedComponent) {
return class SelectableList extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
};
componentWillMount() {
this.setState({
selectedIndex: this.props.defaultValue,
});
this.props.fetchItem();
}
handleRequestChange = (event, index) => {
this.setState({
selectedIndex: index,
});
};
render() {
console.log(this.state.item);
return (
<ComposedComponent
value={this.state.selectedIndex}
onChange={this.handleRequestChange}
>
{this.props.children}
{this.props.item}
</ComposedComponent>
);
}
};
}
SelectableList = wrapState(SelectableList);
class ListSiswa extends Component {
state = {
item: {}
}
getListSiswa(){
fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
.then(posts => {
return posts.json();
}).then(data => {
let item = data.posts.map((itm) => {
return(
<div key={itm.siswa_id}>
<ListItem
value={itm.siswa_id}
primaryText={itm.nama}
/>
</div>
)
});
this.setState({item: item});
});
}
render() {
return (
<SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
<Subheader>Daftar Siswa</Subheader>
</SelectableList>
);
}
}
export default ListSiswa;
Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!
The error is in this component.
const ListSiswa = () => (
<SelectableList>
<Subheader>Daftar Siswa</Subheader>
{this.state.item}
</SelectableList>
);
This component is referred as Stateless Functional Components (Read)
It is simply a pure function which receives some data and returns the jsx.
you do not have the access this here.

Resources