简体   繁体   中英

Redux:Changing the value in store does not trigger my another component using the same value

I have tried other similar suggestions, none work therefore I'm posting my own question. So I have a react js app that is using redux. I need to show a columns dropdown using which I can set a table's columns to show or hide. I have a header component and a list component, both are siblings. I'm trying to change the value of columnList in header component, dispatching update action to update in store and on that basis I'm trying to change columns in the list component. My issue at the moment is that my list component is not rerendering when I change visibility of columnList, nor is it triggering the useEffect. However, what's further confusing me is while inspect in chrome is opened, and once I change the columnList value in header component, moving the partition to either increase or decrease the inspect window shows or hides the columns depending on how I set them(This is the only time when they show/hide).

Below is the list component, irrelevant parts and additional column list value have not been included

const columnList=useSelector(selectColumnList,shallowEqual);

var columns = [
    {
      id:1,
      title: "RPA ID",
      dataIndex: "",
      columnVisible:true,
      render: (record) => {
        let id = record.id.toString().padStart(3, "0");
        return `RPA${id}`;
      },
    },]

  useEffect(()=>{
    console.log("triggering dispatch")
    dispatch(updateOpportunityColumns(columns));
  },[])

const renderBody = (props, columns) => {
      return (
        <tr className={props.className}>
          {columns.map((item, idx) => {
            if (item.columnVisible) {
              return props.children[idx]
            }
          })}
        </tr>
      );
    }

    const renderHeader = (props, columns) => {
      return (
        <tr>
          {columns.map((item, idx) => {
            if (item.columnVisible)
              return props.children[idx];
          })}
        </tr>
      );
    }

  return (
    <div>
      <Table
        rowKey="uid"
        onRow={(record, rowIndex) => {
          return {
            onDoubleClick: () => {
              history.push({
                pathname: `${
                  RouteNames.Opportunity.path
                }/RPA${record.id.toString().padStart(3, "0")}`,
                opportunityId: record.id,
              });
            },
          };
        }}
        columns={columnList}
        dataSource={pagedData}
        pagination={{
          total: total,
          defaultPageSize: pageSize,
          current: currentPage,
          onChange: onPageChange,
        }}
        components={{
          header: {
            row: (props) => renderHeader(props, columnList),
          },
          body: {
            row: (props) => renderBody(props, columnList)
          },
        }}
      />
    </div>
  );
};

Below is my header

var columns=useSelector(selectColumnListIntermediate,shallowEqual)
const [state,setState]=useState();

  function handleDropdownCheckboxClick(i,event){
    event.stopPropagation();
    dispatch(updateOpportunityColumnAtIndex(i));
  }

const menus=Object.entries(columns).map((key,i)=>{
    if(key[1].title){
      return(
        <Menu key={key[0]}>
        <Menu.Item key={key[0]}>
          <Checkbox checked={key[1].columnVisible} onClick={(e)=>{handleDropdownCheckboxClick(i,e)}} >{key[1].title}</Checkbox>
        </Menu.Item>
      </Menu>
  )}})

  const menu = () => {
    return (
      <Menu onClick={""}>
        {menus}
      </Menu>
    )};
  return (
    <div>
      <div style={{ display: "flex", alignItems: "flex-end" }}>
        <Avatar
          style={{ marginRight: "5px" }}
          shape="square"
          size={64}
          src={`${URL}${logoUrl}`}
        />

        <Title style={{ color: "#0AA9F0" }} level={3}>
          {name}
        </Title>
      </div>
      <Divider />

      <div>
        <Button
          className="btn-border"
          onClick={handleClick}
          style={{ width: "300px" }}
          type="primary"
          shape="circle"
          icon={<PlusOutlined />}
        >
          Create Opportunity
        </Button>
        
        
        <Dropdown.Button overlay={menu}>
          Columns
        </Dropdown.Button>

        <div className="userlist">
          <Search
            placeholder="Search"
            allowClear
            onSearch={(value) => onSearch(value)}
            enterButton
          />
        </div>
      </div>
    </div>
  );
};

And finally below is my reducer

case UPDATE_COLUMNS_LIST:
{
       return {
        ...state,
        ColumnListIntermediate:payload,
        ColumnList:payload,
      };}

case UPDATE_COLUMN_LIST_AT_INDEX:{
        const newArray = [...state.ColumnList];
        newArray[action.payload].columnVisible = !state.ColumnList[payload].columnVisible
        return { 
          ...state,
          ColumnList:newArray,
          ColumnListIntermediate:newArray,
         }
      };

and action file

export const getOpportunityColumns = () => ({
  type: GET_COLUMNS_LIST
});

export const getOpportunityColumnsIntermediate = () => ({
  type: GET_COLUMNS_LIST_INTERMEDIATE
});

export const updateOpportunityColumns = (ColumnValue) => ({
  type: UPDATE_COLUMNS_LIST,
  payload: ColumnValue,
});

export const updateOpportunityColumnAtIndex = (index) => ({
  type: UPDATE_COLUMN_LIST_AT_INDEX,
  payload: index,
});

Sorry the files are to big, containing other things to be posted completely.

You were almost there but did are still mutating, you should carefully read and try to understand how to update in an immutable way .

Here is the code that should work:

case UPDATE_COLUMN_LIST_AT_INDEX: {
  //map state.ColumList to new array
  const newArray = state.ColumnList.map((item, index) =>
    index === action.payload
      ? //toggle columVisible if index is action payload
        { ...item, columnVisible: !item.columnVisible }
      : //return item unchanged, index is not action.payload
        item
  );
  return {
    ...state,
    ColumnList: newArray,
    ColumnListIntermediate: newArray,
  };
}

If this didn't work you should check what is getting rendered I don't know what the Table component is but it may not re render when props change because it has a buggy implementation, I know old react bootstrap had some problems.

Thankyou so much for your help, although from here on I just removed "shallowEqual" from the list component,

const columnList=useSelector(selectColumnList,shallowEqual);

to

const columnList=useSelector(selectColumnList);

and in the header aswell, and it worked fine after that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM