conditional component render without using location.pathname React - javascript

I'm currently attempting to render components based off of what page the user is currently on. One of the components remains the same and which one is rendered with it will be determined on page location. I'm currently using window.location.pathname but this is not a desired behavior for us.
const Container = reduxForm({ form: 'login' })(LoginForm)
const currentLocation = window.location.pathname
const ExtraButtons = () => (
<div className="row center-xs">
<div className="col-xs-6 center-xs">
<Link to={FORGOT_PASSWORD_ROUTE}>Forgot Password?</Link>
</div>
</div>
)
const Login = ({ loginStart, error, update, search, address,
currentUser }) => (
<div>
<div className="row center-xs">
<div className="col-xs-12">
<img src={logo} className="logo" />
</div>
</div>
<div className="row">
<div className="col-xs">
<Container onSubmit={loginStart} />
{error && (
<InputError
visible={error && error !== null}
errorMessage={error.message}
/>
)}
<ExtraButtons />
</div>
{currentLocation.includes('/login') ? (
<div className="row center-xs start-xs col-xs">
<LocSearch
updateAddress={update}
search={search}
address={address}
/>
</div>
) : currentLocation.includes('/home') ? (
<div className="col-xs">
<EmailSearch />
</div>
) : null}
</div>
</div>
)
const mapStateToProps = state => {
const { submitting, currentUser } = state.user
const { address = '' } = state.locationSearch
const error = state.error
return {
address,
submitting,
error,
currentUser
}
}
const mapDispatchToProps = {
loginStart,
resetError,
update,
search
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)

I think currentLocation is assigned the value when the script is loaded instead of when your component is rendered. that is the root cause. you should define currentLocation in your component.

Related

How to preserve state of flip card after navigating to another page and then coming back to same page

I tried to preserve the state of flip card, when I flip card I navigated to another route and after coming back from that page the card again come back to its original state, I want to preserve the flipped card state(back side).here is the code first component that renders is Cards and second one is FlippableCard and third one is Cards.
const Cards = () => {
return (
<>
<Navbar></Navbar>
<div className="round-box">Flip a Card</div>
<div className="flex-container">
<Remaincard />
<div className="flex-container-child">
<div className="flex-child">
<FlippableCard title={data[0].cardName} key={0} />
</div>
</div>
</div>
</>
);
};
export default Cards;
function Card({ onClick, title }) {
const navigate = useNavigate();
const timeOutFun = (e) => {
setTimeout(() => navigate("../afterflip/" + title), 300);
console.log(title);
};
return (
<>
<div className="card" onClick={onClick}>
<div className="card-back"></div>
<div className="card-front">
<button
className="middle card-front"
onClick={() => {
timeOutFun();
}}
>
hiii
</button>
<p
onClick={() => {
timeOutFun();
}}
className="text-on-card"
>
{title}
</p>
</div>
</div>
</>
);
}
function FlippableCard({ title, key }) {
const [showFront, setShowFront] = useState(true);
// console.log("showFront");
const [color, setColor] = useState("#110781");
return (
<div className="scroll-remove">
<div className="flippable-card-container">
<CSSTransition in={showFront} timeout={300} classNames="flip">
<Card
title={title}
value={key}
onClick={() => {
setShowFront((v) => !v);
setColor("#A8A8A8");
setShowFront(false);
// getLocalData();
}}
/>
</CSSTransition>
</div>
</div>
);
}
You might want to look into using localStorage.setItem and localStorage.getItem. This will store the data in the browser indefinitely using cache. If you want the data to be deleted or in other words, refreshed, you can use session storage. This retains data until that particular tab is closed.

OnClick event results differently in form and image

I have a navbar component with a search bar and the main page component. When an npm package name is inputted in the search bar it should render the main page component with the details of the package using api.npms.io.
App.jsx
function App() {
const [input, setInput] = useState('react');
return (
<div className="App">
<Navbar setInput={setInput} />
<div className="sections">
<Main input={input} />
</div>
</div>
);
}
export default App;
navbar
function Navbar({ setInput }) {
const inputRef = useRef();
const submitHandler = () => {
setInput(inputRef.current.value);
inputRef.current.value = '';
};
return (
<div className="Navbar" id="Navbar">
<div className="logo">
<img src={logo} alt="logo" />
</div>
<div className="line"></div>
<form onSubmit={submitHandler}>
<div className="search">
<img src={search} alt="search" onClick={submitHandler} />
<input
placeholder={'Search for a NPM package'}
name="name"
type="text"
ref={inputRef}
id="name"
></input>
</div>
</form>
</div>
);
}
export default Navbar;
Main.jsx
function Main({ input }) {
const [packageInfo, setPackageInfo] = useState(null);
const [loading, setLoading] = useState(true);
const fetchPackageInfo = async (input) => {
const response = await fetch(`https://api.npms.io/v2/package/${input}`);
const data = await response.json();
setPackageInfo(data);
setLoading(false);
};
useEffect(() => {
fetchPackageInfo(input);
}, [input]);
return (
<div>
{loading ? (
<Loader />
) : (
<div className="Main" id="Main">
<div className="row">
<Summary
heading={packageInfo.collected.metadata.name}
version={packageInfo.collected.metadata.version}
description={packageInfo.collected.metadata.description}
license={packageInfo.collected.metadata.license}
npm={packageInfo.collected.metadata.links.npm}
github={packageInfo.collected.metadata.links.homepage}
downloads={Math.trunc(
packageInfo.evaluation.popularity.downloadsCount
).toLocaleString()}
keywords={packageInfo.collected.metadata.keywords.join(', ')}
/>
</div>
</div>
)}
</div>
);
}
export default Main;
When I input the value in the search bar and then click on the image or the search icon, it gives me the expected output which is to render the details of the package but while the data is being fetched from the API it does not show the loader.
The next problem is when I submit the inputted value it renders the details of the default package which is react and not the package name entered in the search bar.
Any help would be beneficial. Also, I am new to React and am not sure if I am managing state the right way.
I fixed the problem by adding setLoading(true) before fetching the API and also by adding e.preventDefault() in the submitHandler() function

Can't destructure props in child component

I'm getting some trouble on destructuring props when passing an object to a child component. This object has 2 values, one is another object and the second is a function.
This is my code in case any of you can help:
Parent cmp:
export const AdminScreen = () => {
const {kolorTokenContract, setContract} = useContext(ContractsContext);
console.log("contract from admin: ", kolorTokenContract);
return (
<div className="container">
<h1>Administrator Screen</h1>
<hr />
<div className="row align-items-center">
<ERC20AdminForm props={{kolorTokenContract, setContract}} />
<TokenInfo {...kolorTokenContract} />
</div>
<hr />
<Balance />
</div>
);
};
Child cmp:
export const ERC20AdminForm = ({kolorTokenContract, setContract}) => {
//console.log("props from erc20admin form: ", props);
console.log("contract from erc20admin form: ", kolorTokenContract);
return (
<div className="col-8 col-md-6 col-sm-4 ">
<MintingForm props={kolorTokenContract} />
<SetVaultForm />
<TransferOwnershipForm />
</div>
);
};
If i log the "kolorTokenContract" i just get undefined :(. Thanks in advance for any help!
For Child component:
const ERC20AdminForm = (props:any) => {
console.log("contract from erc20admin form: ", props.kolorTokenContract);
return (
<div className="col-8 col-md-6 col-sm-4 ">
<MintingForm kolorTokenContract={props.kolorTokenContract} />
<SetVaultForm />
<TransferOwnershipForm />
</div>
);
};
Call from a parent with separate props:
<ERC20AdminForm kolorTokenContract={kolorTokenContract} setContract={setContract} />
This is a sandbox code for proof of concept.
In your admin screen you should check for the kolorTokenContract . what does the context return?
from where do you get the value of kolorTokenContract, does it come from an api?
if it is then I am guessing while defining the state you didn't give it a value.
For solution, you can give an empty object while defining the kolorTokenContract in your context or you can set a default value
const {kolorTokenContract = {}, setContract} = useContext(ContractsContext);

useState hook is breaking activePost as useEffect is triggered by selectedPost

My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>

Is it possible to display updated state from useState in another component?

I have this component which uses useState const [answers, setAnswerFunction] = useState(options);
Once the answers state has been updated in this component I would like to use the updated state and display it in another component. Is this possible?
I had a look at a similar question which says to use useContext I have not looked into this yet as I have never used it (Is it possible to share states between components using the useState() hook in React?) but I wondered if there would be a simpler way?
Code:
const QuestionBox = ({
question,
options,
correct,
incrementScore,
incrementResponse,
}) => {
const [response, setResponse] = useState("");
const [answers, setAnswerFunction] = useState(options);
const computeAnswer = answer => {
if (answer === correct) {
setResponse("correct");
incrementScore();
incrementResponse();
} else {
setResponse("sorry wrong!");
incrementResponse();
}
};
return (
<div className="questionBox">
<div className="question"> {question} </div>
{answers.map((answer, index) => {
return (
<button
key={index}
className="answerBtn"
type="button"
onClick={() => {
setAnswerFunction([answer]);
computeAnswer(answer);
}}
>
{answer}
</button>
);
})}
{response === "correct" ? (
<div className="correctResponse"> {response} </div>
) : (
<div className="wrongResponse"> {response} </div>
)}
</div>
);
};
export default QuestionBox;
I want to display the state from the component abover answers here on Result card via the prop userAnswer:
const ResultCard = ({
score,
getQuestions,
qbank,
userAnswer
}) => {
return (
<div>
<div>You scored {score} out of 5! </div>
<div className="playBtnBox">
<button className="playBtn" type="button" onClick={getQuestions}>
Play again
</button>
</div>
<div>
{qbank.map((questionObject) => {
return (
<div>
<div className="questionBox"> {questionObject.question}</div>
<div className="resultCardCorrect"> Correct Answer: {questionObject.correct}</div>
</div>
);
})}
</div>
<div className="resultCardCorrect"> Your Answer: {userAnswer}</div>
</div>
);
};
export default ResultCard;

Categories