Context with Rc<Refcell<T>> reducer type has unexpected behavior
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 38/100
Research direction
Start at the Reducible::reduce implementation and trace how use_reducer and ContextProvider determine whether context subscribers rerender. Reproduce the two StateData implementations from the issue on the wasm32-unknown-unknown target, then inspect existing reducer or context tests if present. Done means the behavior is explained and the subscriber UI updates correctly for the intended state change.
Written by the indexing model from the issue text.
Description
Problem
Thank you for taking the time to read my issue.
I'm not sure if this is my implementation error or a yew bug but I'll try to explain the situation as clear as possible.
I am developing an application where I need a context that requires some fields that are of type Vec<T> so for sake of not copying data around I wrap each struct member in Rc<RefCell<>>.
there are two implementations of Reducible trait that each behave differently.
First implementation:
Create a new Self.
Causes the context subscribers and the context itself to reload when I click either button which is expected behavior.
Second implementation:
Change underlying data and return self.
Causes only the context itself to reload on click and not the subscribers even if underlying data is changed, there for the UI does not update which is not what I want.
How should I approach this problem since I cannot create a new Self on each state update ?
Steps To Reproduce
Code:
use yew::prelude::*;
use gloo::console::log;
use std::rc::Rc;
use std::cell::RefCell;
#[derive(PartialEq)]
pub struct StateData {
pub preview_open: Rc<RefCell<bool>>,
// some other vector data that I dont want to copy on each state change
}
impl Default for StateData {
fn default() -> Self {
Self {
preview_open: Rc::new(RefCell::new(false)),
}
}
}
pub enum StateAction {
SetPreviewOpen(bool),
}
// First implementation
impl Reducible for StateData {
type Action = StateAction;
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
match action {
StateAction::SetPreviewOpen(x) => {
return Self {
preview_open: Rc::new(RefCell::new(x)),
}.into();
}
}
}
}
// Second implementation
// impl Reducible for StateData {
// type Action = StateAction;
//
// fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
// match action {
// StateAction::SetPreviewOpen(x) => {
// *self.preview_open.borrow_mut() = x.into();
// }
// }
//
// self
// }
// }
type ChatroomContext = UseReducerHandle<StateData>;
#[derive(Properties, PartialEq)]
pub struct ChildrenProps {
pub children: Children
}
#[function_component]
pub fn ChatContext(props: &ChildrenProps) -> Html {
let state = use_reducer(|| StateData::default());
log!("reload context");
log!(format!("{}", *state.preview_open.borrow()));
html! {
<ContextProvider<ChatroomContext> context={state} >
{props.children.clone()}
</ContextProvider<ChatroomContext>>
}
}
#[function_component]
fn SubOne() -> Html {
let ctx = use_context::<ChatroomContext>().unwrap();
let onclick = {
let ctx = ctx.clone();
Callback::from(
move |_| {
ctx.dispatch(StateAction::SetPreviewOpen(true));
}
)
};
log!("reload sub one");
html! {
<>
<div>{"SUB One"}</div>
<button {onclick} >{"One button"}</button>
<div>{format!("State from One: {}", ctx.preview_open.borrow().clone().to_string())}</div>
</>
}
}
#[function_component]
fn SubTwo() -> Html {
let ctx = use_context::<ChatroomContext>().unwrap();
let onclick = {
let ctx = ctx.clone();
Callback::from(
move |_| {
ctx.dispatch(StateAction::SetPreviewOpen(false));
}
)
};
log!("reload sub two");
html! {
<>
<div>{"SUB Two"}</div>
<button {onclick} >{"Two button"}</button>
<div>{format!("State from Two: {}", ctx.preview_open.borrow().clone().to_string())}</div>
</>
}
}
#[function_component]
fn App() -> Html {
html! {
<div>
<ChatContext>
<SubOne />
<SubTwo />
</ChatContext>
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
Environment:
- Yew version: [v0.21]
- Rust version: [1.76.0,
stable] - Target, if relevant: [
wasm32-unknown-unknown] - Build tool, if relevant: [
trunk] - OS, if relevant: [
Windows 11] - Browser and version, if relevant: [
Brave 1.62.162 Chromium: 121.0.6167.164 (Official Build) (64-bit)]
Questionnaire
- I'm interested in fixing this myself but don't know where to start
- I would like to fix and I have a solution
- I don't have time to fix this right now, but maybe later
- Dominant language
- Rust
- Stars
- 32.8k
- Forks
- 1.5k
- Avg merge
- 5h 34m
- Merged PRs (30d)
- 2
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from yewstack/yew
-
documentation
Difficulty 3/5 1-2 days Newbie friendliness 70/100
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 68/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
documentation
Difficulty 4/5 3-5 days Newbie friendliness 42/100
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
bevyengine/bevy#25861 ·
-
comp-datalake
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/ClickHouse#121222 ·
-
enhancement remote
Difficulty 2/5 1-3 hours Newbie friendliness 68/100