I have created a counter app in React js using context api for global state management .
But the problem is when i am clicking increase and decrease button it is not updating global values .
I am new to react , please provide guidance what is going wrong here .
ContextFile :
import {createContext,useState} from 'react';
export const DataContext = createContext({
data:0,
increase : () => {},
decrease : () => {}
});
function DataContextProvider(props){
const [data,setData] = useState();
const increase = () => {
setData(data + 1);
}
const decrease = () => {
setData(data - 1);
}
return(
<DataContext.Provider value={{data,increase,decrease}}>
{props.children}
</DataContext.Provider>
);
};
export default DataContextProvider;
App.js :
import React,{useContext} from 'react';
import {DataContext} from './Context/dataContext';
import DataContextProvider from './Context/dataContext';
import IncreaseBtn from './Component/Increase';
import DecreaseBtn from './Component/Decrease';
const App = () => {
const {data} = useContext(DataContext);
return(
<>
<DataContextProvider>
{data}
<br/>
<br/>
<IncreaseBtn />
<br/>
<br/>
<DecreaseBtn />
</DataContextProvider>
</>
)
}
export default App;
Increase Button Component :
import React,{useContext} from 'react';
import {DataContext} from '../Context/dataContext';
const IncreaseBtn = () => {
const {increase} = useContext(DataContext);
return(
<>
<button onClick={increase}> Increase </button>
</>
)
}
export default IncreaseBtn;
Decrease Button Component :
import React,{useContext} from 'react';
import {DataContext} from '../Context/dataContext';
const DecreaseBtn = () => {
const {decrease} = useContext(DataContext);
return(
<>
<button onClick={decrease}> Decrease </button>
</>
)
}
export default DecreaseBtn;
Folder Structure :
If you want to use context you should wrap your provider around those components, but here App component isn't wrapped but to its children 😉
Give an initial state of some "number" as it would be undefined and it gives NaN if you do the arithmetic operations with it.
Updated the sandbox for your ref
You are updating the state in the wrong way
Try:
setCount(count => count + 1);
Related
I'm currently making a simple web frontend with react using react-autosuggest to search a specified user from a list. I want to try and use the Autosuggest to give suggestion when the user's type in the query in the search field; the suggestion will be based on username of github profiles taken from github user API.
What I want to do is to separate the AutoSuggest.jsx and then import it into Main.jsx then render the Main.jsx in App.js, however it keeps giving me 'TypeError: _ref2 is undefined' and always refer to my onChange function of AutoSuggest.jsx as the problem.
Below is my App.js code:
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Header from './views/header/Header';
import Main from './views/main/Main';
import Footer from './views/footer/Footer';
const App = () => {
return (
<>
<Header/>
<Main/> <- the autosuggest is imported in here
<Footer/>
</>
);
}
export default App;
Below is my Main.jsx code:
import React, { useState } from 'react';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import axios from 'axios';
import { useEffect } from 'react';
import AutoSuggest from '../../components/AutoSuggest';
const Main = () => {
const [userList, setUserList] = useState([]);
useEffect(() => {
axios.get('https://api.github.com/users?per_page=100')
.then((res) => setUserList(res.data))
.catch((err) => console.log(err));
}, [])
return (
<Container>
<br/>
<Row>
<AutoSuggest userList={userList} placeHolderText={'wow'} />
</Row>
</Container>
);
}
export default Main;
Below is my AutoSuggest.jsx code:
import React, { useState } from "react";
import Autosuggest from 'react-autosuggest';
function escapeRegexCharacters(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function getSuggestions(value, userList) {
const escapedValue = escapeRegexCharacters(value.trim());
if (escapedValue === '') {
return [];
}
const regex = new RegExp('^' + escapedValue, 'i');
return userList.filter(user => regex.test(user.login));
}
function getSuggestionValue(suggestion) {
return suggestion.name;
}
function renderSuggestion(suggestion) {
return (
<span>{suggestion.name}</span>
);
}
const AutoSuggest = ({userList, placeHolderText}) => {
const [value, setValue] = useState('');
const [suggestions, setSuggestions] = useState([]);
const onChange = (event, { newValue, method }) => { <- error from console always refer here, I'm not quite sure how to handle it..
setValue(newValue);
};
const onSuggestionsFetchRequested = ({ value }) => {
setValue(getSuggestions(value, userList))
};
const onSuggestionsClearRequested = () => {
setSuggestions([]);
};
const inputProps = {
placeholder: placeHolderText,
value,
onChange: () => onChange()
};
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={() => onSuggestionsFetchRequested()}
onSuggestionsClearRequested={() => onSuggestionsClearRequested()}
getSuggestionValue={() => getSuggestionValue()}
renderSuggestion={() => renderSuggestion()}
inputProps={inputProps} />
);
}
export default AutoSuggest;
The error on browser (Firefox) console:
I have no idea what does the error mean or how it happened and therefore unable to do any workaround.. I also want to ask if what I do here is already considered a good practice or not and maybe some inputs on what I can improve as well to make my code cleaner and web faster. Any input is highly appreciated, thank you in advance!
you have to write it like this... do not use the arrow function in inputProps
onChange: onChange
SOLUTION JAVASCRIPT:
How create to Build a “like button” component using React 16. The component should be the default export (use export default).
Click here image
Personally, I prefer to use functional components instead of using class-based components. One solution to your problem could be the following code.
import React, { useState } from 'react';
const LikeButton = () => {
const [likes, setLikes] = useState(100);
const [isClicked, setIsClicked] = useState(false);
const handleClick = () => {
if (isClicked) {
setLikes(likes - 1);
} else {
setLikes(likes + 1);
}
setIsClicked(!isClicked);
};
return (
<button className={ `like-button ${isClicked && 'liked'}` } onClick={ handleClick }>
<span className="likes-counter">{ `Like | ${likes}` }</span>
</button>
);
};
export default LikeButton;
I am facing a problem with re-rendering after a state change in my NextJS app.
The function sendMessageForm launches a redux action sendMessage which adds the message to the state.
The problem is unrelated to the returned state in the reducer as I am returning a new object(return {...state}) which should trigger the re-render!
Is there anything that might block the re-render ?
This is the file that calls & displays the state, so no other file should be responsible ! But if you believe the problem might lie somewhere else, please do mention !
import { AttachFile, InsertEmoticon, Mic, MoreVert } from '#mui/icons-material';
import { Avatar, CircularProgress, IconButton } from '#mui/material';
import InfiniteScroll from 'react-infinite-scroller';
import Head from 'next/head';
import { useState, useEffect } from 'react';
import Message from '../../components/Message.component';
import styles from '../../styles/Chat.module.css'
import { useRouter } from 'next/router'
import {useSelector, useDispatch} from "react-redux"
import {bindActionCreators} from "redux"
import * as chatActions from "../../state/action-creators/chatActions"
const Chat = () => {
const router = useRouter()
const { roomId } = router.query
const auth = useSelector((state)=> state.auth)
const messages = useSelector((state)=> state.chat[roomId].messages)
const dispatch = useDispatch()
const {getMessages, markAsRead, sendMessage} = bindActionCreators(chatActions, dispatch)
const [inputValue, setInputValue] = useState("")
const sendMessageForm = (e) => {
e.preventDefault()
console.log("***inputValue:", inputValue)
sendMessage(roomId, inputValue)
}
const loadMessages = (page) => {
if(roomId)
getMessages(roomId, page)
}
//user-read-message
useEffect(() => {
//user-read-message
markAsRead(roomId, auth.user._id)
}, [messages]);
return (
<div className={styles.container}>
<Head>
<title>Chat</title>
</Head>
<div className={styles.header}>
<Avatar/>
<div className={styles.headerInformation}>
<h3>Zabre el Ayr</h3>
<p>Last Seen ...</p>
</div>
<div className={styles.headerIcons}>
<IconButton>
<AttachFile/>
</IconButton>
<IconButton>
<MoreVert/>
</IconButton>
</div>
</div>
<div className={styles.chatContainer}>
<InfiniteScroll
isReverse={true}
pageStart={0}
loadMore={loadMessages}
hasMore={messages.hasNextPage || false}
loader={<div className={styles.loader} key={0}><CircularProgress /></div>}
>
{Object.keys(messages.docs).map((key, index)=>{
return<Message
key={index}
sentByMe={messages.docs[key].createdBy === auth.user._id}
message={messages.docs[key].msg}
/>})}
</InfiniteScroll>
<span className={styles.chatContainerEnd}></span>
</div>
<form className={styles.inputContainer}>
<InsertEmoticon/>
<input className={styles.chatInput} value={inputValue} onChange={(e)=>setInputValue(e.target.value)}/>
<button hidden disabled={!inputValue} type='submit' onClick={sendMessageForm}></button>
<Mic/>
</form>
</div>)
};
export default Chat;
useSelector requires a new object with a new reference from the object you are passing to it in order to trigger the re-render
What you're doing with return {...state} is just creating a new object for the parent object but not the nested one useSelector is using, which is in your case :
const messages = useSelector((state)=> state.chat[roomId].messages)
So, you should return the whole state as a new object WITH a new state.chat[roomId].messages object
In other words, the references for the root object & the one being used should be changed.
I have a searchbar component and a Catalog component. The Catalog component contains different cards. Depending on what is typed in the input field of the searchbar component I want to render different cards.
For this to work I need to be able to import the value of the input field into the Catalog component where it is passed in a search function that handles all the rest of the work.
I am able to import the value into my Catalog component but unfortunaty I can't figure out how I can tell if the imported value has changed so I can search again?
I have found some ways to do this with classes but I would like to use hooks instead. I have experimented a bit with "useEffect" but that didn't work out.
Thank you for your help!
This is my code in the searchbar component:
import React, { useState } from 'react';
let input = "";
function Search() {
const [value, setValue] = useState(input);
function onSearch(e) {
setValue(e.target.value);
input = value;
}
return(
<form className="searchForm">
<input className="search" type="text" name="search" autoComplete="off" placeholder="zoeken" value={value} onChange={onSearch}/> </form>
);
}
export { Search, input };
And this is the code in my Catalog
import React, { useState, useEffect } from 'react';
import {input} from "./search";
// other imports
function Catalog(props){
//get cards code and fuse code
const [query, setQuery] = useState(input);
function inputHasChanged(){ //function that can tell if the imported input variable changed
setQuery(input); //update query and rerender cards
}
const results = fuse.search(query)
const searchedCards = query ? results.map(card => card.item) : cards;
//other code
return(
<div>
//render the SearchedCards
</div>
);
}
export {Catalog};
Solution:
code in search:
import React, { useState } from 'react';
const Search = ({ searching }) => {
const [value, setValue] = useState("");
function submit(e){
setValue(e.target.value);
searching(value);
}
return (
<form className="searchForm">
<input
className="search"
type="text" name="search"
autoComplete="off"
placeholder="zoeken"
value={value}
onChange={submit}
/>
</form>
);
};
export { Search };
Search is a child of banner:
import React, {useState, useEffect} from 'react';
import {Search} from './search';
import Header from './Header';
import Overzicht from './Overzicht';
const Banner = ({ search }) => {
const [value, setValue] = useState("");
useEffect(() => {
search(value);
},[search, value]);
return(
<div className="banner">
<Header />
<Search searching={value => setValue(value)} />
<Overzicht />
</div>
);
};
export default Banner;
Banner is a child of home which also contains Catalog:
import React, { useState } from "react";
import Banner from './banner';
import {Catalog} from './Catalog';
function Home(){
const [input, setInput] = useState("");
return(
<div>
<section id="banner">
<Banner search={input => setInput(input)}/>
</section>
<section id="overzicht">
<Catalog search={input} />
</section>
</div>
);
}
export default Home;
And now I can just call
props.search
In Catalog
You can use useEffect as mentioned below:
useEffect(() => {
// Write your logic here
},[input]); // it will run only when the input changes
Push the common state, the query, up to a common ancestor and pass it down as needed to child and descendant components. This way they can "watch" the changes by having new props passed to them.
Below is a simplified version of a structure that would work:
function Catalog({ query }) {
const [results, setResults] = useState(null);
useEffect(() => {
// If `fuse.search` is asynchronous then you might need to debounce
// these queries and/or cancel old queries. If a user types "foo",
// a query is kicked off, and then they finish typing "food", you
// want to cancel the query for "foo" because the results will no
// longer be relevant.
const results = fuse.search(query);
setResults(results);
}, [query])
return (
<div />
);
}
function Search({ query, setQuery }) {
return (
<input onChange={setQuery} value={query} />
)
}
function App() {
const [query, setQuery] = useState("");
return (
<>
<Search query={query} setQuery={setQuery} />
<Catalog query={query} />
</>
);
}
import React, { useEffect, useState } from 'react';
import { Card } from 'components/Card';
import { dateFilter } from 'helpers';
import Chart from 'chart.js';
import 'chartjs-chart-matrix';
import chroma from 'chroma-js';
import moment from 'moment';
const WeeklyTrafficCard = (props) => {
const { start, end, data, store } = props;
const capacity = store && store.capacity;
var numberOfweeks = 0; //representing how many weeks back
const dateArray = [];
var today = moment();
while (numberOfweeks < 10) {
var from_date = today.startOf('week').format('MM/DD/YY');
var to_date = today.endOf('week').format('MM/DD/YY');
var range = from_date.concat(' ','-',' ',to_date);
dateArray.push(range);
today = today.subtract(7, 'days');
numberOfweeks++;
//console.log(dateArray);
}
const [each_daterange, setDateRange] = useState();
I have this Component called WeeklyTrafficCard and I want to use the variable, each_daterange, in another component, which imported WeeklyTrafficCard as below to send the get request, clearly I cannot use each_daterange directly right here, how I can work around it?
import React, { useContext, useEffect, useState } from 'react';
import { WeeklyTrafficCard } from './WeeklyTrafficCard';
import { AppContext } from 'contexts/App';
import { API_URL } from 'constants/index.js';
import { todayOpen, todayClose } from 'helpers';
import moment from 'moment';
const WeeklyTrafficCardContainer = (props) => {
const { API } = useContext(AppContext);
const { store = {} } = props;
const [data, setData] = useState([]);
const open = todayOpen(store.hours, store.timezone);
const close = todayClose(store.hours, store.timezone);
useEffect(() => {
async function fetchData() {
const result = await API.get(`${API_URL}/api/aggregates`, {
params: {
each_daterange,
every: '1h',
hourStart: 13,
hourStop: 4
},
});
You should use a useEffect(prop drilling) to pass your variable in your parent:
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const [myVar, setMyVar] = React.useState('');
return (
<div>
<Child setMyVar={setMyVar} />
{myVar}
</div>
);
};
const Child = ({setMyVar}) => {
const myChildVar = "Hello world !"
React.useEffect( () => setMyVar(myChildVar),[]);
return <div> This is the child</div>
}
render(<App />, document.getElementById("root"));
Here is the repro on stackblitz
Understanding of the Problem
You want to pass data up to the parent from the child.
Manage each_daterange in the parent:
Instead of creating your useState variable each_daterange in the child you can declare it in the parent and pass down it's setter function. For instance:
const WeeklyTrafficCardContainer = (props) => {
const [eachDateRange, setEachDateRange] = useState();
return (
<div>
{/* your return */}
<WeeklyTrafficCard setEachDateRange={setEachDateRange} />
</div>
)
}
If you need to display eachDateRange in the traffic card, or the traffic card needs to completely own that variable, you can create another state variable in the parent and pass a callback to the child (essentially what is above but now you have two different state variables).
The parent becomes
const WeeklyTrafficCardContainer = (props) => {
const [requestDateRange, setRequestDateRange] = useState();
const updateRequestDateRange = (dateRange) => {
setRequestDateRange(dateRange)
}
return (
<div>
{/* your return */}
<WeeklyTrafficCard updateDateRange={updateRequestDateRange} />
</div>
)
}
Then in your WeeklyTrafficCard call props.updateDateRange and pass it the date range whenever each_daterange changes.
Ciao, of course you need a global state manager. My preferred is react-redux. In few word, react-redux allows you to have a state that is shared in all your components. Sharing each_daterange between WeeklyTrafficCardContainer and WeeklyTrafficCard will be very easy if you decide to use it.
This is the more appropriate guide to quick start with react-redux. have a nice coding :)
Keep the value outside of the component, where both can access it. There are other ways to do this, but just as a simple example you could create a simple "store" to hold it and reference that store from each component that needs it:
class Store {
setDateRange (newDateRange) {
this._dateRange = newDateRange;
}
get dateRange () {
return this._dateRange;
}
}
export default new Store(); // singleton; everyone gets the same instance
import store from './Store';
const WeeklyTrafficCard = (props) => {
// use current dateRange value
const dateRange = store.dateRange;
// set new dateRange
store.setDateRange( newDateRange );
// do other stuff
}
import store from './Store';
const WeeklyTrafficCardContainer = (props) => {
// use current dateRange value
const dateRange = store.dateRange;
// set new dateRange
store.setDateRange( newDateRange );
// do other stuff
}
If you want store updates to trigger component re-renders you'd need to add some higher order component plumbing, like redux's connect, or some other mechanism for triggering updates:
// pseudocode; make store an event emitter and return
// a component that re-renders on store events
store.connect = Component => {
return props => {
React.useEffect(() => {
store.addEventListener( ... )
return () => store.removeEventListener( ... )
})
}
}
Or if the components share a common parent, you could lift the state to the parent and pass the information to each component as props. If either component updates the value, the parent state change will trigger a re-render of both components with the new value:
const Parent = () => {
const [dateRange, setDateRange] = React.useState();
return (
<>
<WeeklyTrafficCardContainer
dateRange={dateRange}
onDateRangeChange={newRange => setDateRange(newRange)}
/>
<WeeklyTrafficCard
dateRange={dateRange}
onDateRangeChange={newRange => setDateRange(newRange)}
/>
</>
);
}
Let's rephrase the objective here.
Objective: access each_daterange from WeeklyTrafficCard component in WeeklyTrafficCardContainer component.
Note: simply put, choose the following case based on your problem.
choose using prop if the variable is to be accessed by only one component
choose using context if the variable is to be accessed by more than one components
Solution Cases:
Case A: using prop.
Case A.1. WeeklyTrafficCard is the parent of WeeklyTrafficCardContainer
each_datarange being passed from WeeklyTrafficCard component as prop to WeeklyTrafficCardContainer component
working example for reference: codesandbox - variable passed as prop
// WeeklyTrafficCard.jsx file
const WeeklyTrafficCard = () => {
const [each_daterange, setDateRange] = useState();
return (
<>
...
<WeeklyTrafficCardContainer eachDateRange={each_daterange} />
</>
);
};
// WeeklyTrafficCardContainer.jsx file
const WeeklyTrafficCardContainer = props => {
const eachDateRange = props.eachDateRange;
return (
<>
...
</>
);
};
Case A.2. WeeklyTrafficCard & WeeklyTrafficCardContainer are children of a parent, say WeeklyTraffic component
each_datarange will be present in WeeklyTraffic component which is shared among WeeklyTrafficCard component & WeeklyTrafficCardContainer component
// WeeklyTraffic.jsx file
const WeeklyTraffic = () => {
const [each_daterange, setDateRange] = useState();
return (
<>
...
<WeeklyTrafficCard eachDateRange={each_daterange} />
<WeeklyTrafficCardContainer eachDateRange={each_daterange} />
</>
);
};
// WeeklyTrafficCard.jsx file
const WeeklyTrafficCard = props => {
const eachDateRange = props.eachDateRange;
return (
<>
...
</>
);
};
// WeeklyTrafficCardContainer.jsx file
const WeeklyTrafficCardContainer = props => {
const eachDateRange = props.eachDateRange;
return (
<>
...
</>
);
};
Case B: using context.
follow blog example found: blog - react context
this is preferred way to implement if the variable/variables is/are shared or need to be accessed by more than 1 components